Donc vous ne pouvez pas faire ça. Utilisez les fonctions normales.
DrTyrsa
1
Quel est l'intérêt de donner un nom à une fonction anonyme?
John La Rooy
2
@gnibbler Vous pouvez utiliser le nom pour faire référence à la fonction. y () est plus facile à utiliser que (lambda: 0) () dans le REPL.
Thomas Jung
Alors, quel est l'avantage de y=lambda...plus def y:alors?
John La Rooy
@gnibbler Un peu de contexte: je voulais définir une fonction def g (f, e) qui appelle f dans le cas heureux et e si une erreur était détectée. Selon le scénario, e peut déclencher une exception ou renvoyer une valeur valide. Pour utiliser g, je voulais écrire g (lambda x: x * 2, lambda e: lever e) ou encore g (lambda x: x * 2, lambda e: 0).
Thomas Jung
Réponses:
169
Il existe plus d'une façon de skin un Python:
y =lambda:(_ for _ in()).throw(Exception('foobar'))
Les lambdas acceptent les déclarations. Puisqu'il raise exs'agit d'une instruction, vous pouvez écrire un relanceur à usage général:
def raise_(ex):raise ex
y =lambda: raise_(Exception('foobar'))
Mais si votre objectif est d'éviter un def, cela ne le coupe évidemment pas. Il vous permet cependant de déclencher des exceptions conditionnelles, par exemple:
y =lambda x:2*x if x <10else raise_(Exception('foobar'))
Vous pouvez également lever une exception sans définir de fonction nommée. Tout ce dont vous avez besoin est un estomac solide (et 2.x pour le code donné):
Si vous ne vous souciez pas ce type d'exception est levée, ce qui suit fonctionne aussi: lambda: 1 / 0. Vous vous retrouverez simplement avec une erreur ZeroDivisionError au lieu d'une exception normale. Gardez à l'esprit que si l'exception est autorisée à se propager, il peut sembler étrange à quelqu'un de déboguer votre code de commencer à voir un tas de ZeroDivisionErrors.
Warren Spencer
Excellente solution @WarrenSpencer. La plupart du code n'a pas beaucoup d'erreurs de division zéro, il est donc aussi distinctif que si vous pouviez choisir le type vous-même.
jwg le
2
y = 1/0est super solution intelligente si le type d'exception est hors de propos
Saher Ahwal
3
Quelqu'un peut-il nous expliquer ce qui se passe réellement dans les solutions «art sombre / estomac fort»?
C'est assez piraté mais pour écrire des tests où vous voulez vous moquer des fonctions cela fonctionne très bien !!!
Kannan Ekanath
8
Fonctionne mais vous ne devriez pas le faire.
août
1
Cela ne fonctionne pas pour moi, j'obtiens un SyntaxErrorsur Python 2.7.11.
Nick Sweeting
J'obtiens également l'erreur ci-dessus (SyntaxError) sur Python 2.7.5
Dinesh
1
c'est spécifique à python 3 mais je ne pense pas que python 2 le permette.
Saher Ahwal
16
En fait, il y a un moyen, mais c'est très artificiel.
Vous pouvez créer un objet code à l'aide de la compile()fonction intégrée. Cela vous permet d'utiliser l' raiseinstruction (ou toute autre instruction, d'ailleurs), mais cela soulève un autre défi: exécuter l'objet code. La manière habituelle serait d'utiliser l' execinstruction, mais cela vous ramène au problème d'origine, à savoir que vous ne pouvez pas exécuter des instructions dans un lambda(ou un eval(), d'ailleurs).
La solution est un hack. Les appelables comme le résultat d'une lambdainstruction ont tous un attribut __code__, qui peut en fait être remplacé. Ainsi, si vous créez un appelable et remplacez sa __code__valeur par l'objet de code ci-dessus, vous obtenez quelque chose qui peut être évalué sans utiliser d'instructions. Cependant, réaliser tout cela entraîne un code très obscur:
map(lambda x, y, z: x.__setattr__(y, z) or x, [lambda: 0], ["__code__"], [compile("raise Exception", "", "single"])[0]()
Ce qui précède fait ce qui suit:
l' compile()appel crée un objet code qui déclenche l'exception;
le lambda: 0retourne un appelable qui ne fait que renvoyer la valeur 0 - ceci est utilisé pour exécuter l'objet de code ci-dessus plus tard;
le lambda x, y, zcrée une fonction qui appelle la __setattr__méthode du premier argument avec les arguments restants, ET RENVOIE LE PREMIER ARGUMENT! Cela est nécessaire, car __setattr__lui - même revient None;
l' map()appel prend le résultat de lambda: 0, et l'utilisation de lambda x, y, zremplace son __code__objet par le résultat de l' compile()appel. Le résultat de cette opération de carte est une liste avec une entrée, celle renvoyée par lambda x, y, z, c'est pourquoi nous en avons besoin lambda: si nous l'utilisions __setattr__tout de suite, nous perdrions la référence à l' lambda: 0objet!
enfin, le premier (et unique) élément de la liste renvoyée par l' map()appel est exécuté, ce qui entraîne l' appel de l'objet code, levant finalement l'exception souhaitée.
Cela fonctionne (testé en Python 2.6), mais ce n'est certainement pas joli.
Une dernière remarque: si vous avez accès au typesmodule (ce qui nécessiterait d'utiliser l' importinstruction avant votre eval), vous pouvez raccourcir un peu ce code: en utilisant, types.FunctionType()vous pouvez créer une fonction qui exécutera l'objet de code donné, vous avez donc gagné 'pas besoin de créer une fonction factice avec lambda: 0et de remplacer la valeur de son __code__attribut.
C'est vrai, mais cela ne répond pas vraiment à la question; lever une exception à partir d'une expression lambda est facile à faire (et l'attraper peut parfois être réalisé en utilisant des astuces très artificielles, voir stackoverflow.com/a/50916686/2560053 ou stackoverflow.com/a/50961836/2560053 ).
Thomas Baruchel
15
Si tout ce que vous voulez est une expression lambda qui déclenche une exception arbitraire, vous pouvez le faire avec une expression illégale. Par exemple, lambda x: [][0]essaiera d'accéder au premier élément d'une liste vide, ce qui déclenchera une IndexError.
VEUILLEZ NOTER : Ceci est un hack, pas une fonctionnalité. Ne l' utilisez dans aucun code (non codé-golf) qu'un autre être humain pourrait voir ou utiliser.
Dans mon cas , je reçois: TypeError: <lambda>() takes exactly 1 positional argument (2 given). Êtes-vous sûr de l'IndexError?
Jovik
4
Oui. Avez-vous peut-être fourni le mauvais nombre d'arguments? Si vous avez besoin d'une fonction lambda pouvant accepter n'importe quel nombre d'arguments, utilisez lambda *x: [][0]. (La version originale ne prend qu'un seul argument; pour aucun argument, utilisez lambda : [][0]; pour deux, utilisez lambda x,y: [][0]; etc.)
Kyle Strand
3
J'ai développé un peu ceci: lambda x: {}["I want to show this message. Called with: %s" % x] Produit: KeyError: 'I want to show this message. Called with: foo'
ErlVolton
@ErlVolton Clever! Bien que l'utiliser n'importe où sauf dans un script ponctuel semble être une idée terrible ...
Kyle Strand
J'utilise temporairement des tests unitaires pour un projet où je n'ai pas pris la peine de faire une vraie simulation de mon enregistreur. Il se déclenche si vous essayez de consigner une erreur ou une erreur critique. Alors ... Oui terrible, bien que consensuel :)
ErlVolton
10
J'aimerais donner une explication de la MISE À JOUR 3 de la réponse fournie par Marcelo Cantos:
nous appelons le constructeur de l'objet code avec les arguments suivants:
argcount = 1
kwonlyargcount = 0
nlocals = 1
stacksize = 1
drapeaux = 67
codestring = b '| \ 0 \ 202 \ 1 \ 0'
constantes = ()
noms = ()
varnames = ('x',)
nom de fichier = ''
nom = ''
firstlineno = 1
lnotab = b ''
Vous pouvez lire ce que signifient les arguments dans la définition du PyCodeObjecthttps://github.com/python/cpython/blob/master/Include/code.h . La valeur de 67 pour l' flagsargument est par exemple CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE.
L'argument le plus important est celui codestringqui contient les opcodes d'instructions. Voyons ce qu'ils signifient.
LOAD_FAST(var_num)Pushes a reference to the local co_varnames[var_num] onto the stack.
Nous poussons donc la référence xsur la pile. Le varnamesest une liste de chaînes contenant uniquement «x». Nous allons pousser le seul argument de la fonction que nous définissons dans la pile.
L'octet suivant est l'opcode pour RAISE_VARARGSet l'octet suivant est son argument, c'est-à-dire 1.
RAISE_VARARGS(argc)Raises an exception using one of the 3 forms of the raise statement, depending on the value of argc:0:raise(re-raise previous exception)1:raise TOS (raise exception instance or type at TOS)2:raise TOS1 from TOS (raise exception instance or type at TOS1 with __cause__ set to TOS)
Le TOS est le top-of-stack. Puisque nous avons poussé le premier argument ( x) de notre fonction dans la pile et qu'il argcvaut 1, nous lèverons le
xs'il s'agit d'une instance d'exception ou créerons une instance de xet la lèverons autrement.
Le dernier octet, c'est-à-dire 0, n'est pas utilisé. Ce n'est pas un opcode valide. Cela pourrait aussi bien ne pas être là.
Revenons à l'extrait de code que nous sommes en train de faire:
Appelons help sur un objet fonction pour voir ce que signifient les arguments.
Help on class function in module builtins:class function(object)| function(code, globals, name=None, argdefs=None, closure=None)||Create a function object.|| code
| a code object
| globals
| the globals dictionary
| name
| a string that overrides the name from the code object
| argdefs
| a tuple that specifies the default argument values
| closure
| a tuple that supplies the bindings for free variables
Nous appelons ensuite la fonction construite en passant une instance d'Exception comme argument. Par conséquent, nous avons appelé une fonction lambda qui lève une exception. Exécutons l'extrait et voyons qu'il fonctionne effectivement comme prévu.
>>> type(lambda:0)(type((lambda:0).__code__)(...1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}...)(Exception())Traceback(most recent call last):File"<stdin>", line 3,in<module>File"", line 1,inException
Améliorations
Nous avons vu que le dernier octet du bytecode est inutile. N'encombrons pas inutilement cette expression compliquée. Supprimons cet octet. Aussi, si nous voulons jouer un peu au golf, nous pourrions omettre l'instanciation d'Exception et passer à la place la classe Exception comme argument. Ces modifications entraîneraient le code suivant:
y=lambda...
plusdef y:
alors?Réponses:
Il existe plus d'une façon de skin un Python:
Les lambdas acceptent les déclarations. Puisqu'il
raise ex
s'agit d'une instruction, vous pouvez écrire un relanceur à usage général:Mais si votre objectif est d'éviter un
def
, cela ne le coupe évidemment pas. Il vous permet cependant de déclencher des exceptions conditionnelles, par exemple:Vous pouvez également lever une exception sans définir de fonction nommée. Tout ce dont vous avez besoin est un estomac solide (et 2.x pour le code donné):
Et une solution forte pour l'estomac python3 :
Merci d'avoir signalé @WarrenSpencer une réponse très simple si vous ne vous inquiétez pas quelle exception est soulevée:
y = lambda: 1/0
.la source
lambda: 1 / 0
. Vous vous retrouverez simplement avec une erreur ZeroDivisionError au lieu d'une exception normale. Gardez à l'esprit que si l'exception est autorisée à se propager, il peut sembler étrange à quelqu'un de déboguer votre code de commencer à voir un tas de ZeroDivisionErrors.y = 1/0
est super solution intelligente si le type d'exception est hors de proposQue diriez-vous:
la source
SyntaxError
sur Python 2.7.11.En fait, il y a un moyen, mais c'est très artificiel.
Vous pouvez créer un objet code à l'aide de la
compile()
fonction intégrée. Cela vous permet d'utiliser l'raise
instruction (ou toute autre instruction, d'ailleurs), mais cela soulève un autre défi: exécuter l'objet code. La manière habituelle serait d'utiliser l'exec
instruction, mais cela vous ramène au problème d'origine, à savoir que vous ne pouvez pas exécuter des instructions dans unlambda
(ou uneval()
, d'ailleurs).La solution est un hack. Les appelables comme le résultat d'une
lambda
instruction ont tous un attribut__code__
, qui peut en fait être remplacé. Ainsi, si vous créez un appelable et remplacez sa__code__
valeur par l'objet de code ci-dessus, vous obtenez quelque chose qui peut être évalué sans utiliser d'instructions. Cependant, réaliser tout cela entraîne un code très obscur:map(lambda x, y, z: x.__setattr__(y, z) or x, [lambda: 0], ["__code__"], [compile("raise Exception", "", "single"])[0]()
Ce qui précède fait ce qui suit:
l'
compile()
appel crée un objet code qui déclenche l'exception;le
lambda: 0
retourne un appelable qui ne fait que renvoyer la valeur 0 - ceci est utilisé pour exécuter l'objet de code ci-dessus plus tard;le
lambda x, y, z
crée une fonction qui appelle la__setattr__
méthode du premier argument avec les arguments restants, ET RENVOIE LE PREMIER ARGUMENT! Cela est nécessaire, car__setattr__
lui - même revientNone
;l'
map()
appel prend le résultat delambda: 0
, et l'utilisation delambda x, y, z
remplace son__code__
objet par le résultat de l'compile()
appel. Le résultat de cette opération de carte est une liste avec une entrée, celle renvoyée parlambda x, y, z
, c'est pourquoi nous en avons besoinlambda
: si nous l'utilisions__setattr__
tout de suite, nous perdrions la référence à l'lambda: 0
objet!enfin, le premier (et unique) élément de la liste renvoyée par l'
map()
appel est exécuté, ce qui entraîne l' appel de l'objet code, levant finalement l'exception souhaitée.Cela fonctionne (testé en Python 2.6), mais ce n'est certainement pas joli.
Une dernière remarque: si vous avez accès au
types
module (ce qui nécessiterait d'utiliser l'import
instruction avant votreeval
), vous pouvez raccourcir un peu ce code: en utilisant,types.FunctionType()
vous pouvez créer une fonction qui exécutera l'objet de code donné, vous avez donc gagné 'pas besoin de créer une fonction factice aveclambda: 0
et de remplacer la valeur de son__code__
attribut.la source
Les fonctions créées avec des formulaires lambda ne peuvent pas contenir d'instructions .
la source
Si tout ce que vous voulez est une expression lambda qui déclenche une exception arbitraire, vous pouvez le faire avec une expression illégale. Par exemple,
lambda x: [][0]
essaiera d'accéder au premier élément d'une liste vide, ce qui déclenchera une IndexError.VEUILLEZ NOTER : Ceci est un hack, pas une fonctionnalité. Ne l' utilisez dans aucun code (non codé-golf) qu'un autre être humain pourrait voir ou utiliser.
la source
TypeError: <lambda>() takes exactly 1 positional argument (2 given)
. Êtes-vous sûr de l'IndexError?lambda *x: [][0]
. (La version originale ne prend qu'un seul argument; pour aucun argument, utilisezlambda : [][0]
; pour deux, utilisezlambda x,y: [][0]
; etc.)lambda x: {}["I want to show this message. Called with: %s" % x]
Produit:KeyError: 'I want to show this message. Called with: foo'
J'aimerais donner une explication de la MISE À JOUR 3 de la réponse fournie par Marcelo Cantos:
Explication
lambda: 0
est une instance de labuiltins.function
classe.type(lambda: 0)
est labuiltins.function
classe.(lambda: 0).__code__
est uncode
objet.Un
code
objet est un objet qui contient le bytecode compilé entre autres. Il est défini ici en CPython https://github.com/python/cpython/blob/master/Include/code.h . Ses méthodes sont implémentées ici https://github.com/python/cpython/blob/master/Objects/codeobject.c . Nous pouvons exécuter l'aide sur l'objet code:type((lambda: 0).__code__)
est la classe de code.Alors quand on dit
nous appelons le constructeur de l'objet code avec les arguments suivants:
Vous pouvez lire ce que signifient les arguments dans la définition du
PyCodeObject
https://github.com/python/cpython/blob/master/Include/code.h . La valeur de 67 pour l'flags
argument est par exempleCO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE
.L'argument le plus important est celui
codestring
qui contient les opcodes d'instructions. Voyons ce qu'ils signifient.La documentation des opcodes peut être trouvée ici https://docs.python.org/3.8/library/dis.html#python-bytecode-instructions . Le premier octet est l'opcode pour
LOAD_FAST
, le second octet est son argument c'est à dire 0.Nous poussons donc la référence
x
sur la pile. Levarnames
est une liste de chaînes contenant uniquement «x». Nous allons pousser le seul argument de la fonction que nous définissons dans la pile.L'octet suivant est l'opcode pour
RAISE_VARARGS
et l'octet suivant est son argument, c'est-à-dire 1.Le TOS est le top-of-stack. Puisque nous avons poussé le premier argument (
x
) de notre fonction dans la pile et qu'ilargc
vaut 1, nous lèverons lex
s'il s'agit d'une instance d'exception ou créerons une instance dex
et la lèverons autrement.Le dernier octet, c'est-à-dire 0, n'est pas utilisé. Ce n'est pas un opcode valide. Cela pourrait aussi bien ne pas être là.
Revenons à l'extrait de code que nous sommes en train de faire:
Nous avons appelé le constructeur de l'objet code:
On passe l'objet code et un dictionnaire vide au constructeur d'un objet fonction:
Appelons help sur un objet fonction pour voir ce que signifient les arguments.
Nous appelons ensuite la fonction construite en passant une instance d'Exception comme argument. Par conséquent, nous avons appelé une fonction lambda qui lève une exception. Exécutons l'extrait et voyons qu'il fonctionne effectivement comme prévu.
Améliorations
Nous avons vu que le dernier octet du bytecode est inutile. N'encombrons pas inutilement cette expression compliquée. Supprimons cet octet. Aussi, si nous voulons jouer un peu au golf, nous pourrions omettre l'instanciation d'Exception et passer à la place la classe Exception comme argument. Ces modifications entraîneraient le code suivant:
Lorsque nous l'exécutons, nous obtiendrons le même résultat qu'avant. C'est juste plus court.
la source