Comment puis-je écrire une expression lambda qui est équivalente à :
def x():
raise Exception()
Ce qui suit n'est pas autorisé :
y = lambda : raise Exception()
Comment puis-je écrire une expression lambda qui est équivalente à :
def x():
raise Exception()
Ce qui suit n'est pas autorisé :
y = lambda : raise Exception()
Il y a plus d'une façon d'écorcher un python :
y = lambda: (_ for _ in ()).throw(Exception('foobar'))
Les lambdas acceptent les déclarations. Puisque raise ex
est une déclaration, vous pourriez rédiger une déclaration d'intérêt général :
def raise_(ex):
raise ex
y = lambda: raise_(Exception('foobar'))
Mais si votre but est d'éviter un def
il est évident que cela ne suffit pas. Elle vous permet cependant de lever des exceptions de manière conditionnelle, par exemple :
y = lambda x: 2*x if x < 10 else raise_(Exception('foobar'))
Vous pouvez également lever une exception sans définir une fonction nommée. Tout ce dont vous avez besoin est un estomac solide (et 2.x pour le code donné) :
type(lambda:0)(type((lambda:0).func_code)(
1,1,1,67,'|\0\0\202\1\0',(),(),('x',),'','',1,''),{}
)(Exception())
Et un python3 estomac solide solution :
type(lambda: 0)(type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
)(Exception())
Merci @WarrenSpencer d'avoir indiqué une réponse très simple si vous ne vous souciez pas de l'exception levée : y = lambda: 1/0
.
Si vous ne vous souciez pas du type d'exception déclenchée, la méthode suivante fonctionne également : lambda: 1 / 0
. Vous vous retrouverez avec une ZeroDivisionError lancée au lieu d'une exception normale. Gardez à l'esprit que si l'exception est autorisée à se propager, il peut sembler étrange à quelqu'un déboguant votre code de commencer à voir un tas de ZeroDivisionErrors.
Excellente solution @WarrenSpencer. La plupart des codes n'ont pas beaucoup d'erreurs de division par zéro, donc c'est aussi distinctif que si vous pouviez choisir le type vous-même.
C'est un peu compliqué mais pour écrire des tests où vous voulez simuler des fonctions, cela fonctionne très bien ! !!
En fait, il y a un moyen, mais il est très artificiel.
Vous pouvez créer un objet de code à l'aide de la fonction compile()
fonction intégrée. Cela vous permet d'utiliser le raise
(ou toute autre instruction, d'ailleurs), mais elle soulève un autre problème : l'exécution de l'objet code. La méthode habituelle consiste à utiliser la fonction exec
mais cela nous ramène au problème initial, à savoir qu'il est impossible d'exécuter des instructions dans un fichier de type lambda
(ou un eval()
d'ailleurs).
La solution est un hack. Les appelables comme le résultat d'un lambda
ont tous un attribut __code__
qui peuvent en fait être remplacés. Ainsi, si vous créez un callable et que vous remplacez sa fonction __code__
avec l'objet de code ci-dessus, vous obtenez quelque chose qui peut être évalué sans utiliser d'instructions. Cependant, tout ceci donne lieu à 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 :
el compile()
crée un objet de code qui soulève l'exception ;
el lambda: 0
renvoie un appelable qui ne fait rien d'autre que de retourner la valeur 0 -- ceci est utilisé pour exécuter l'objet de code ci-dessus plus tard ;
el lambda x, y, z
crée une fonction qui appelle le __setattr__
du premier argument avec les autres arguments, ET RENVOIE LE PREMIER ARGUMENT ! Ceci est nécessaire, car __setattr__
retourne lui-même None
;
el map()
prend le résultat de lambda: 0
et en utilisant le lambda x, y, z
remplace le __code__
avec le résultat de l'opération compile()
appel. Le résultat de cette opération de mappage est une liste avec une seule entrée, celle retournée par lambda x, y, z
c'est pourquoi nous avons besoin de cette lambda
: si nous utilisions __setattr__
tout de suite, nous perdrions la référence à la lambda: 0
Objet !
enfin, le premier (et seul) élément de la liste renvoyée par la fonction map()
est exécuté, ce qui a pour effet d'appeler l'objet de code, et finalement de lever l'exception souhaitée.
Cela fonctionne (testé dans Python 2.6), mais ce n'est certainement pas très joli.
Une dernière remarque : si vous avez accès à l'application types
(ce qui nécessiterait d'utiliser le module import
avant votre eval
), alors 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é, de sorte que vous n'aurez pas besoin de créer une fonction fictive avec la commande lambda: 0
et en remplaçant la valeur de son __code__
attribut.
Si tout ce que vous voulez est une expression lambda qui soulève une exception arbitraire, vous pouvez accomplir ceci avec une expression illégale. Par exemple, lambda x: [][0]
tentera d'accéder au premier élément d'une liste vide, ce qui provoquera une IndexError.
VEUILLEZ NOTER : C'est un hack, pas une fonctionnalité. Ne pas utilisez ceci dans tout code (non codé) qu'un autre être humain pourrait voir ou utiliser.
Dans mon cas, j'obtiens : TypeError: <lambda>() takes exactly 1 positional argument (2 given)
. Etes-vous sûr de l'IndexError ?
Yep. Vous avez peut-être fourni le mauvais nombre d'arguments ? Si vous avez besoin d'une fonction lambda qui peut prendre n'importe quel nombre d'arguments, utilisez lambda *x: [][0]
. (La version originale ne prend qu'un seul argument ; pour ne pas avoir d'argument, utilisez lambda : [][0]
; pour deux, utilisez lambda x,y: [][0]
; etc.)
Je l'ai un peu élargi : lambda x: {}["I want to show this message. Called with: %s" % x]
Produit : KeyError: 'I want to show this message. Called with: foo'
Fonctions créées avec des formulaires lambda ne peut contenir de déclarations .
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 contournées, cf. stackoverflow.com/a/50916686/2560053 o stackoverflow.com/a/50961836/2560053 ).
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
6 votes
Donc vous ne pouvez pas faire ça. Utilisez les fonctions normales.
2 votes
Quel est l'intérêt de donner un nom à une fonction anonyme ?
3 votes
@gnibbler Vous pouvez utiliser le nom pour faire référence à la fonction. y() est plus facile à utiliser que (lambda : 0)() dans le REPL.
2 votes
Quel est donc l'avantage de
y=lambda...
endef y:
alors ?1 votes
@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 a été détectée. Selon le scénario, e peut lever une exception ou retourner une valeur valide. Pour utiliser g, je voulais écrire g(lambda x : x *2, lambda e : raise e) ou alternativement g(lambda x : x * 2, lambda e : 0).
0 votes
Juste au cas où quelqu'un d'autre viendrait ici à la recherche d'un moyen de lever une exception dans le cadre d'un monkeypatch pytest (ou équivalent), j'ai fini par utiliser le monkeypatch pour toucher un fixture qui utilisait l'argument side_effect de Mock comme ceci :
def mock_urlopen(): return Mock(spec=urllib2.urlopen, side_effect=urllib2.URLError('Fake URLError Exception'))
0 votes
Si vous êtes arrivé ici parce que vous voulez lever une exception dans un mock, alors utilisez : side_effect()
0 votes
@JohnLaRooy Pour la postérité, il n'y en a pas. Les lambdas nommés sont une mauvaise pratique . Utilisez un
def
à la place.