Entonces no puedes hacer eso. Utiliza funciones normales.
DrTyrsa
1
¿Cuál es el punto de dar un nombre a una función anónima?
John La Rooy el
2
@gnibbler Puede usar el nombre para referirse a la función. y () es más fácil de usar que (lambda: 0) () en REPL.
Thomas Jung
Entonces, ¿cuál es la ventaja de y=lambda...más de def y:entonces?
John La Rooy
@gnibbler Algún contexto: quería definir una función def g (f, e) que llame a f en el caso feliz y e si se detecta un error. Dependiendo del escenario, e podría generar una excepción o devolver algún valor válido. Para usar g, quería escribir g (lambda x: x * 2, lambda e: raise e) o alternativamente g (lambda x: x * 2, lambda e: 0).
Thomas Jung
Respuestas:
169
Hay más de una forma de pelar un Python:
y =lambda:(_ for _ in()).throw(Exception('foobar'))
Lambdas acepta declaraciones. Dado que raise exes una declaración, podría escribir un recaudador de propósito general:
def raise_(ex):raise ex
y =lambda: raise_(Exception('foobar'))
Pero si su objetivo es evitar un def, obviamente esto no es suficiente. Sin embargo, le permite generar excepciones condicionalmente, por ejemplo:
y =lambda x:2*x if x <10else raise_(Exception('foobar'))
Alternativamente, puede generar una excepción sin definir una función con nombre. Todo lo que necesitas es un estómago fuerte (y 2.x para el código dado):
Si no le importa qué tipo de excepción se produce, el siguiente también funciona: lambda: 1 / 0. Simplemente terminarás con un ZeroDivisionError en lugar de una excepción normal. Tenga en cuenta que si se permite que la excepción se propague, puede parecer extraño que alguien depure su código para comenzar a ver un montón de ZeroDivisionErrors.
Warren Spencer
Gran solución @WarrenSpencer. La mayoría del código no tiene muchos errores de división cero, por lo que es tan distintivo como si pudieras elegir el tipo tú mismo.
jwg
2
y = 1/0es una solución súper inteligente si el tipo de excepción es irrelevante
Saher Ahwal
3
¿Alguien puede hablarnos sobre lo que realmente está sucediendo en las soluciones de 'arte oscuro / estómago fuerte'?
Es bastante hacky, pero para escribir pruebas en las que desea burlarse de funciones, ¡esto funciona bien!
Kannan Ekanath
8
Funciona pero no deberías hacerlo.
augurar
1
Esto no funciona para mí, obtengo un SyntaxErrorPython 2.7.11.
Nick Sweeting
También recibo el error anterior (SyntaxError) en Python 2.7.5
Dinesh
1
Esto es específico de Python 3, sin embargo, no creo que Python 2 lo permita.
Saher Ahwal
16
En realidad, hay una manera, pero es muy artificial.
Puede crear un objeto de código utilizando la compile()función incorporada. Esto le permite usar la raisedeclaración (o cualquier otra declaración, para el caso), pero plantea otro desafío: ejecutar el objeto de código. La forma habitual sería usar la execdeclaración, pero eso lo lleva de regreso al problema original, es decir, que no puede ejecutar declaraciones en un lambda(o un eval(), para el caso).
La solución es un hack. Las llamadas como el resultado de una lambdadeclaración tienen un atributo __code__, que en realidad se puede reemplazar. Entonces, si crea un invocable y reemplaza su __code__valor con el objeto de código de arriba, obtendrá algo que puede evaluarse sin usar declaraciones. Sin embargo, lograr todo esto da como resultado un código muy oscuro:
map(lambda x, y, z: x.__setattr__(y, z) or x, [lambda: 0], ["__code__"], [compile("raise Exception", "", "single"])[0]()
Lo anterior hace lo siguiente:
la compile()llamada crea un objeto de código que genera la excepción;
las lambda: 0declaraciones de un desembolsadas que no hace más que devolver el valor 0 - esto se utiliza para ejecutar el objeto código más adelante;
el lambda x, y, zcrea una función que llama al __setattr__método del primer argumento con el resto de argumentos, y devuelve el primer argumento! Esto es necesario, porque en __setattr__sí mismo regresa None;
la map()llamada toma el resultado de lambda: 0, y el uso de lambda x, y, zreemplaza su __code__objeto con el resultado de la compile()llamada. El resultado de esta operación de mapa es una lista con una entrada, la que devuelve lambda x, y, z, por lo que necesitamos esto lambda: si lo __setattr__usáramos de inmediato, ¡perderíamos la referencia al lambda: 0objeto!
finalmente, map()se ejecuta el primer (y único) elemento de la lista devuelto por la llamada, lo que da como resultado que se llame al objeto de código, lo que finalmente genera la excepción deseada.
Funciona (probado en Python 2.6), pero definitivamente no es bonito.
Una última nota: si tiene acceso al typesmódulo (lo que requeriría usar la importdeclaración antes que su eval), entonces puede acortar un poco este código: usando types.FunctionType()puede crear una función que ejecutará el objeto de código dado, por lo que ganó No necesita el truco de crear una función ficticia lambda: 0y reemplazar el valor de su __code__atributo.
Si todo lo que desea es una expresión lambda que genere una excepción arbitraria, puede lograr esto con una expresión ilegal. Por ejemplo, lambda x: [][0]intentará acceder al primer elemento en una lista vacía, lo que generará un IndexError.
TENGA EN CUENTA : este es un truco, no una característica. No use esto en ningún código (que no sea de código de golf) que otro ser humano pueda ver o usar.
En mi caso me sale: TypeError: <lambda>() takes exactly 1 positional argument (2 given). ¿Estás seguro del IndexError?
Jovik
44
Sí. ¿Quizás proporcionaste el número incorrecto de argumentos? Si necesita una función lambda que pueda tomar cualquier número de argumentos, use lambda *x: [][0]. (La versión original solo toma un argumento; para ningún argumento, use lambda : [][0]; para dos, use lambda x,y: [][0]; etc.)
Kyle Strand
3
He ampliado esto un poco: lambda x: {}["I want to show this message. Called with: %s" % x] Produce: KeyError: 'I want to show this message. Called with: foo'
ErlVolton
@ErlVolton Clever! Aunque usar esto en cualquier lugar, excepto en un guión único, parece una idea terrible ...
Kyle Strand
Estoy usando temporalmente pruebas unitarias para un proyecto en el que no me he molestado en burlarme de mi registrador. Se eleva si intenta registrar un error o crítico. Entonces ... Sí terrible, aunque consensuado :)
ErlVolton
10
Me gustaría dar una explicación de la ACTUALIZACIÓN 3 de la respuesta proporcionada por Marcelo Cantos:
LOAD_FAST(var_num)Pushes a reference to the local co_varnames[var_num] onto the stack.
Entonces empujamos la referencia a xla pila. El varnameses una lista de cadenas que contiene solo 'x'. Llevaremos el único argumento de la función que estamos definiendo a la pila.
El siguiente byte es el código de operación para RAISE_VARARGSy el siguiente byte es su argumento, es decir, 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)
El TOS es el mejor de la pila. Dado que empujamos el primer argumento ( x) de nuestra función a la pila y argces 1, elevaremos el
xsi es una instancia de excepción o haremos una instancia dex y lo elevaremos de otra manera.
El último byte, es decir, 0 no se utiliza. No es un código de operación válido. Bien podría no estar allí.
Volviendo al fragmento de código, estamos analizando:
Llamemos a la ayuda sobre un objeto de función para ver qué significan los argumentos.
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
Luego llamamos a la función construida pasando una instancia de Excepción como argumento. En consecuencia, llamamos a una función lambda que genera una excepción. Ejecutemos el fragmento y veamos que realmente funciona según lo previsto.
>>> 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
Mejoras
Vimos que el último byte del bytecode es inútil. No abarrotemos esta complicada expresión innecesariamente. Eliminemos ese byte. Además, si queremos jugar un poco al golf, podríamos omitir la creación de instancias de Exception y, en su lugar, pasar la clase Exception como argumento. Esos cambios darían como resultado el siguiente código:
y=lambda...
más dedef y:
entonces?Respuestas:
Hay más de una forma de pelar un Python:
Lambdas acepta declaraciones. Dado que
raise ex
es una declaración, podría escribir un recaudador de propósito general:Pero si su objetivo es evitar un
def
, obviamente esto no es suficiente. Sin embargo, le permite generar excepciones condicionalmente, por ejemplo:Alternativamente, puede generar una excepción sin definir una función con nombre. Todo lo que necesitas es un estómago fuerte (y 2.x para el código dado):
Y una solución fuerte para el estómago python3 :
Gracias @WarrenSpencer por señalar una respuesta muy simple, si no le importa qué excepción se plantea:
y = lambda: 1/0
.fuente
lambda: 1 / 0
. Simplemente terminarás con un ZeroDivisionError en lugar de una excepción normal. Tenga en cuenta que si se permite que la excepción se propague, puede parecer extraño que alguien depure su código para comenzar a ver un montón de ZeroDivisionErrors.y = 1/0
es una solución súper inteligente si el tipo de excepción es irrelevanteQué tal si:
fuente
SyntaxError
Python 2.7.11.En realidad, hay una manera, pero es muy artificial.
Puede crear un objeto de código utilizando la
compile()
función incorporada. Esto le permite usar laraise
declaración (o cualquier otra declaración, para el caso), pero plantea otro desafío: ejecutar el objeto de código. La forma habitual sería usar laexec
declaración, pero eso lo lleva de regreso al problema original, es decir, que no puede ejecutar declaraciones en unlambda
(o uneval()
, para el caso).La solución es un hack. Las llamadas como el resultado de una
lambda
declaración tienen un atributo__code__
, que en realidad se puede reemplazar. Entonces, si crea un invocable y reemplaza su__code__
valor con el objeto de código de arriba, obtendrá algo que puede evaluarse sin usar declaraciones. Sin embargo, lograr todo esto da como resultado un código muy oscuro:map(lambda x, y, z: x.__setattr__(y, z) or x, [lambda: 0], ["__code__"], [compile("raise Exception", "", "single"])[0]()
Lo anterior hace lo siguiente:
la
compile()
llamada crea un objeto de código que genera la excepción;las
lambda: 0
declaraciones de un desembolsadas que no hace más que devolver el valor 0 - esto se utiliza para ejecutar el objeto código más adelante;el
lambda x, y, z
crea una función que llama al__setattr__
método del primer argumento con el resto de argumentos, y devuelve el primer argumento! Esto es necesario, porque en__setattr__
sí mismo regresaNone
;la
map()
llamada toma el resultado delambda: 0
, y el uso delambda x, y, z
reemplaza su__code__
objeto con el resultado de lacompile()
llamada. El resultado de esta operación de mapa es una lista con una entrada, la que devuelvelambda x, y, z
, por lo que necesitamos estolambda
: si lo__setattr__
usáramos de inmediato, ¡perderíamos la referencia allambda: 0
objeto!finalmente,
map()
se ejecuta el primer (y único) elemento de la lista devuelto por la llamada, lo que da como resultado que se llame al objeto de código, lo que finalmente genera la excepción deseada.Funciona (probado en Python 2.6), pero definitivamente no es bonito.
Una última nota: si tiene acceso al
types
módulo (lo que requeriría usar laimport
declaración antes que sueval
), entonces puede acortar un poco este código: usandotypes.FunctionType()
puede crear una función que ejecutará el objeto de código dado, por lo que ganó No necesita el truco de crear una función ficticialambda: 0
y reemplazar el valor de su__code__
atributo.fuente
Las funciones creadas con formularios lambda no pueden contener sentencias .
fuente
Si todo lo que desea es una expresión lambda que genere una excepción arbitraria, puede lograr esto con una expresión ilegal. Por ejemplo,
lambda x: [][0]
intentará acceder al primer elemento en una lista vacía, lo que generará un IndexError.TENGA EN CUENTA : este es un truco, no una característica. No use esto en ningún código (que no sea de código de golf) que otro ser humano pueda ver o usar.
fuente
TypeError: <lambda>() takes exactly 1 positional argument (2 given)
. ¿Estás seguro del IndexError?lambda *x: [][0]
. (La versión original solo toma un argumento; para ningún argumento, uselambda : [][0]
; para dos, uselambda x,y: [][0]
; etc.)lambda x: {}["I want to show this message. Called with: %s" % x]
Produce:KeyError: 'I want to show this message. Called with: foo'
Me gustaría dar una explicación de la ACTUALIZACIÓN 3 de la respuesta proporcionada por Marcelo Cantos:
Explicación
lambda: 0
es una instancia de labuiltins.function
clasetype(lambda: 0)
es labuiltins.function
clase(lambda: 0).__code__
Es uncode
objeto.Un
code
objeto es un objeto que contiene el bytecode compilado entre otras cosas. Se define aquí en CPython https://github.com/python/cpython/blob/master/Include/code.h . Sus métodos se implementan aquí https://github.com/python/cpython/blob/master/Objects/codeobject.c . Podemos ejecutar la ayuda en el objeto de código:type((lambda: 0).__code__)
es la clase de códigoEntonces cuando decimos
Estamos llamando al constructor del objeto de código con los siguientes argumentos:
Puede leer sobre lo que significan los argumentos en la definición de
PyCodeObject
https://github.com/python/cpython/blob/master/Include/code.h . El valor de 67 para elflags
argumento es, por ejemploCO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE
.El argumento más importante es el
codestring
que contiene códigos de operación de instrucciones. Veamos a qué se refieren.La documentación de los códigos de operación se puede encontrar aquí https://docs.python.org/3.8/library/dis.html#python-bytecode-instructions . El primer byte es el código de operación para
LOAD_FAST
, el segundo byte es su argumento, es decir, 0.Entonces empujamos la referencia a
x
la pila. Elvarnames
es una lista de cadenas que contiene solo 'x'. Llevaremos el único argumento de la función que estamos definiendo a la pila.El siguiente byte es el código de operación para
RAISE_VARARGS
y el siguiente byte es su argumento, es decir, 1.El TOS es el mejor de la pila. Dado que empujamos el primer argumento (
x
) de nuestra función a la pila yargc
es 1, elevaremos elx
si es una instancia de excepción o haremos una instancia dex
y lo elevaremos de otra manera.El último byte, es decir, 0 no se utiliza. No es un código de operación válido. Bien podría no estar allí.
Volviendo al fragmento de código, estamos analizando:
Llamamos al constructor del objeto de código:
Pasamos el objeto de código y un diccionario vacío al constructor de un objeto de función:
Llamemos a la ayuda sobre un objeto de función para ver qué significan los argumentos.
Luego llamamos a la función construida pasando una instancia de Excepción como argumento. En consecuencia, llamamos a una función lambda que genera una excepción. Ejecutemos el fragmento y veamos que realmente funciona según lo previsto.
Mejoras
Vimos que el último byte del bytecode es inútil. No abarrotemos esta complicada expresión innecesariamente. Eliminemos ese byte. Además, si queremos jugar un poco al golf, podríamos omitir la creación de instancias de Exception y, en su lugar, pasar la clase Exception como argumento. Esos cambios darían como resultado el siguiente código:
Cuando lo ejecutamos obtendremos el mismo resultado que antes. Es solo más corto.
fuente