¿Cuál es más preferible utilizar en Python: funciones lambda o funciones anidadas (‘def’)?

Principalmente utilizo funciones lambda, pero a veces uso funciones anidadas que parecen proporcionar el mismo comportamiento.

Aquí hay algunos ejemplos triviales donde funcionalmente hacen lo mismo si se encontraran dentro de otra función:

Función lambda

>>> a = lambda x : 1 + x >>> a(5) 6 

Función anidada

 >>> def b(x): return 1 + x >>> b(5) 6 

¿Hay ventajas de usar uno sobre el otro? (¿Rendimiento? ¿Legibilidad? ¿Limitaciones? ¿Consistencia? Etc.)

¿Incluso importa? Si no lo hace, entonces eso viola el principio de Pythonic:

“Debería haber una, y preferiblemente solo una, manera obvia de hacerlo” .

Si necesita asignar la lambda a un nombre, use una def lugar. def son solo azúcar sintáctica para una tarea, por lo que el resultado es el mismo, y son mucho más flexibles y legibles.

lambda s puede usarse para usar una vez, desechar funciones que no tienen nombre.

Sin embargo, este caso de uso es muy raro. Rara vez necesita pasar objetos de función sin nombre.

El map() y el filter() incorporados necesitan objetos de función, pero las comprensiones de lista y las expresiones del generador generalmente son más legibles que esas funciones y pueden cubrir todos los casos de uso, sin la necesidad de lambdas.

Para los casos en que realmente necesita un objeto de pequeña función, debe usar las funciones del módulo operator , como operator.add lugar de lambda x, y: x + y

Si aún necesita algo de lambda no cubierto, puede considerar escribir una def , solo para que sea más legible. Si la función es más compleja que las del módulo del operator , una def es probablemente mejor.

Por lo tanto, los casos de uso de lambda buena en el mundo real son muy raros.

En la práctica, para mí hay dos diferencias:

La primera es sobre lo que hacen y lo que devuelven:

  • def es una palabra clave que no devuelve nada y crea un ‘nombre’ en el espacio de nombres local.

  • lambda es una palabra clave que devuelve un objeto de función y no crea un ‘nombre’ en el espacio de nombres local.

Por lo tanto, si necesita llamar a una función que toma un objeto de función, la única forma de hacerlo en una línea de código de Python es con un lambda. No hay equivalente con def.

En algunos marcos esto es bastante común; por ejemplo, uso mucho Twisted , y así hago algo como

 d.addCallback(lambda result: setattr(self, _someVariable, result)) 

Es bastante común, y más conciso con las lambdas.

La segunda diferencia es sobre lo que la función real puede hacer.

  • Una función definida con ‘def’ puede contener cualquier código de Python
  • Una función definida con ‘lambda’ tiene que evaluar una expresión y, por lo tanto, no puede contener instrucciones como imprimir, importar, boost, …

Por ejemplo,

 def p(x): print x 

funciona como se espera, mientras

 lambda x: print x 

es un SyntaxError.

Por supuesto, hay soluciones alternativas: sustituya la print por sys.stdout.write , o import con __import__ . Pero normalmente es mejor ir con una función en ese caso.

En esta entrevista, Guido van Rossum dice que desearía no haber dejado ‘lambda’ en Python:

P. ¿Con qué característica de Python estás menos satisfecho?

A veces he sido demasiado rápido en aceptar contribuciones y luego me di cuenta de que era un error. Un ejemplo sería algunas de las funciones de progtwigción funcional, como las funciones lambda. lambda es una palabra clave que le permite crear una pequeña función anónima; funciones incorporadas como mapear, filtrar y reducir ejecutar una función sobre un tipo de secuencia, como una lista.

En la práctica, no resultó tan bien. Python solo tiene dos ámbitos: local y global. Esto hace que la escritura de las funciones lambda sea dolorosa, porque a menudo desea acceder a las variables en el ámbito donde se definió la lambda, pero no puede debido a los dos ámbitos. Hay una forma de evitar esto, pero es algo así como una confusión. A menudo, parece mucho más fácil en Python usar un bucle for en lugar de jugar con las funciones lambda. el mapa y los amigos funcionan bien solo cuando ya hay una función incorporada que hace lo que usted quiere.

En mi humilde opinión, Iambdas puede ser conveniente a veces, pero por lo general son convenientes a expensas de la legibilidad. ¿Puedes decirme qué hace esto?

 str(reduce(lambda x,y:x+y,map(lambda x:x**x,range(1,1001))))[-10:] 

Lo escribí, y me tomó un minuto entenderlo. Esto es del Proyecto Euler. No diré cuál es el problema porque odio los spoilers, pero se ejecuta en 0.124 segundos 🙂

Para n = 1000 aquí hay un tiempo para llamar a una función frente a una lambda:

 In [11]: def f(a, b): return a * b In [12]: g = lambda x, y: x * y In [13]: %%timeit -n 100 for a in xrange(n): for b in xrange(n): f(a, b) ....: 100 loops, best of 3: 285 ms per loop In [14]: %%timeit -n 100 for a in xrange(n): for b in xrange(n): g(a, b) ....: 100 loops, best of 3: 298 ms per loop In [15]: %%timeit -n 100 for a in xrange(n): for b in xrange(n): (lambda x, y: x * y)(a, b) ....: 100 loops, best of 3: 462 ms per loop 

Estoy de acuerdo con el consejo de nosklo: si necesita asignar un nombre a la función, use def . lambda funciones lambda para los casos en los que simplemente paso un breve fragmento de código a otra función, por ejemplo:

 a = [ (1,2), (3,4), (5,6) ] b = map( lambda x: x[0]+x[1], a ) 

Actuación:

Crear una función con lambda es un poco más rápido que crearla con def . La diferencia se debe a que def crea una entrada de nombre en la tabla de locales. La función resultante tiene la misma velocidad de ejecución.


Legibilidad:

Las funciones Lambda son algo menos legibles para la mayoría de los usuarios de Python, pero también son mucho más concisas en algunas circunstancias. Considere la posibilidad de convertir el uso de rutina no funcional a funcional:

 # Using non-functional version. heading(math.sqrt(vx * vx + vy * vy), math.atan(vy / vx)) # Using lambda with functional version. fheading(v, lambda v: math.sqrt(vx * vx + vy * vy), lambda v: math.atan(vy / vx)) # Using def with functional version. def size(v): return math.sqrt(vx * vx + vy * vy) def direction(v): return math.atan(vy / vx) deal_with_headings(v, size, direction) 

Como puede ver, la versión lambda es más corta y “más fácil” en el sentido de que solo necesita agregar lambda v: a la versión no funcional original para convertirla a la versión funcional. También es mucho más conciso. Pero recuerde, la syntax lambda confundirá a muchos usuarios de Python, de modo que lo que pierda en longitud y complejidad real se recuperará en la confusión de otros progtwigdores.


Limitaciones:

  • lambda funciones lambda solo se pueden usar una vez, a menos que se asignen a un nombre de variable.
  • lambda funciones lambda asignadas a nombres de variables no tienen ninguna ventaja sobre las funciones def .
  • lambda funciones lambda pueden ser difíciles o imposibles de encurtir.
  • los nombres de las funciones def deben ser elegidos cuidadosamente para ser razonablemente descriptivos y únicos o, al menos, no utilizados en su scope.

Consistencia:

Python evita en su mayoría las convenciones de progtwigción funcional en favor de una semántica objetiva de procedimiento y más simple. El operador lambda está en contraste directo con este sesgo. Además, como una alternativa a la def ya prevalece, la función lambda agrega diversidad a su syntax. Algunos lo considerarían menos consistente.


Funciones preexistentes:

Como han señalado otros, muchos usos del lambda en el campo pueden ser reemplazados por miembros del operator u otros módulos. Por ejemplo:

 do_something(x, y, lambda x, y: x + y) do_something(x, y, operator.add) 

El uso de la función preexistente puede hacer que el código sea más legible en muchos casos.


El principio de Pythonic: “Debe haber una, y preferiblemente una sola, manera obvia de hacerlo”

Eso es similar a la única fuente de la doctrine de la verdad . Desafortunadamente, el principio de una sola manera obvia de hacerlo siempre ha sido más una aspiración melancólica para Python, en lugar de un verdadero principio guía. Considera las muy poderosas comprensiones de matriz en Python. Son funcionalmente equivalentes a las funciones de map y filter :

 [e for e in some_array if some_condition(e)] filter(some_array, some_condition) 

lambda y def son iguales.

Es una cuestión de opinión, pero diría que cualquier cosa en el lenguaje Python para uso general que obviamente no rompe nada es lo suficientemente “Pythonic”.

El uso principal de lambda siempre ha sido para funciones de callback simples, y para map, reduce, filter, que requieren una función como argumento. Con la comprensión de la lista se está convirtiendo en la norma, y ​​el agregado permitido si como en:

 x = [f for f in range(1, 40) if f % 2] 

es difícil imaginar un caso real para el uso de lambda en el uso diario. Como resultado, diría, evitar lambda y crear funciones anidadas.

Una limitación importante de las lambdas es que no pueden contener nada más que una expresión. Es casi imposible que una expresión lambda produzca algo más que efectos secundarios triviales, ya que no puede tener un cuerpo tan rico como una función definida.

Dicho esto, Lua influyó en mi estilo de progtwigción hacia el uso extensivo de funciones anónimas, y yo ensucio mi código con ellas. Además de eso, tiendo a pensar en mapear / reducir como operadores abstractos de una manera que no considero comprensiones de lista o generadores, casi como si estuviera aplazando una decisión de implementación explícitamente al usar esos operadores.

Edit: Esta es una pregunta bastante antigua, y mis opiniones al respecto han cambiado, en cierto modo.

En primer lugar, estoy fuertemente predispuesto a asignar una expresión lambda a una variable; Python tiene una syntax especial solo para eso (hint, def ). Además de eso, muchos de los usos de lambda, incluso cuando no reciben un nombre, tienen implementaciones predefinidas (y más eficientes). Por ejemplo, el ejemplo en cuestión se puede abreviar a solo (1).__add__ , sin la necesidad de envolverlo en un lambda o def . Muchos otros usos comunes pueden satisfacerse con alguna combinación de los módulos de operator , itertools y functools .

Si bien está de acuerdo con las otras respuestas, a veces es más legible. Aquí hay un ejemplo en el que la lambda es útil, en un caso de uso que me encuentro con un valor defaultdict N dimensiones.
Aquí hay un ejemplo:

 from collections import defaultdict d = defaultdict(lambda: defaultdict(list)) d['Foo']['Bar'].append(something) 

Me parece más legible que crear una def para la segunda dimensión. Esto es aún más significativo para las dimensiones más altas.

Más preferible: funciones lambda o funciones anidadas ( def )?

Hay una ventaja de usar un lambda sobre una función regular (se crean en una expresión), y varios inconvenientes. Por esa razón, prefiero crear funciones con la palabra clave def lugar de con lambdas.

Primer punto – son el mismo tipo de objeto

Un resultado lambda en el mismo tipo de objeto que una función regular

 >>> l = lambda: 0 >>> type(l)  >>> def foo(): return 0 ... >>> type(foo)  >>> type(foo) is type(l) True 

Dado que las lambdas son funciones, son objetos de primera clase.

Tanto lambdas como funciones:

  • se puede pasar como un argumento (igual que una función regular)
  • cuando se crea dentro de una función externa, se convierte en un cierre sobre los locales de las funciones externas

Pero las lambdas faltan, de manera predeterminada, algunas cosas que las funciones obtienen a través de la syntax de definición de la función completa.

El __name__ de lamba es ''

Las Lambdas son funciones anónimas, después de todo, por lo que no saben su propio nombre.

 >>> l.__name__ '' >>> foo.__name__ 'foo' 

Por lo tanto, no se puede buscar lambda programáticamente en su espacio de nombres.

Esto limita ciertas cosas. Por ejemplo, foo puede consultarse con un código serializado, mientras que l no puedo:

 >>> import pickle >>> pickle.loads(pickle.dumps(l)) Traceback (most recent call last): File "", line 1, in  _pickle.PicklingError: Can't pickle  at 0x7fbbc0464e18>: attribute lookup  on __main__ failed 

Podemos buscar foo bien, porque conoce su propio nombre:

 >>> pickle.loads(pickle.dumps(foo))  

Lambdas no tiene anotaciones ni docstring.

Básicamente, las lambdas no están documentadas. Vamos a reescribir foo para estar mejor documentado:

 def foo() -> int: """a nullary function, returns 0 every time""" return 0 

Ahora, foo tiene documentación:

 >>> foo.__annotations__ {'return': } >>> help(foo) Help on function foo in module __main__: foo() -> int a nullary function, returns 0 every time 

Considerando que, no tenemos el mismo mecanismo para dar la misma información a lambdas:

 >>> help(l) Help on function  in module __main__:  lambda (...) 

Pero podemos piratearlos:

 >>> l.__doc__ = 'nullary -> 0' >>> l.__annotations__ = {'return': int} >>> help(l) Help on function  in module __main__:  lambda ) -> in nullary -> 0 

Sin embargo, es probable que haya algún error que estropee la salida de la ayuda.

Lambdas solo puede devolver una expresión.

Lambdas no puede devolver declaraciones complejas, solo expresiones.

 >>> lambda: if True: 0 File "", line 1 lambda: if True: 0 ^ SyntaxError: invalid syntax 

Las expresiones pueden ser bastante complejas, y si te esfuerzas mucho , probablemente puedas lograr lo mismo con un lambda, pero la complejidad añadida es más en detrimento de escribir un código claro.

Utilizamos Python para mayor claridad y facilidad de mantenimiento. El uso excesivo de lambdas puede trabajar en contra de eso.

El único lado positivo para las lambdas: se puede crear en una sola expresión

Este es el único alza posible. Ya que puede crear un lambda con una expresión, puede crearlo dentro de una llamada de función.

La creación de una función dentro de una llamada de función evita la búsqueda de nombre (barata) frente a una creada en otro lugar.

Sin embargo, dado que Python se evalúa estrictamente, no hay otra ganancia de rendimiento al hacerlo, aparte de evitar la búsqueda de nombres.

Para una expresión muy simple, podría elegir un lambda.

También tiendo a usar lambdas cuando hago Python interactivo, para evitar múltiples líneas cuando uno lo hace. Utilizo el siguiente tipo de formato de código cuando quiero pasar un argumento a un constructor cuando llamo a timeit.repeat :

 import timeit def return_nullary_lambda(return_value=0): return lambda: return_value def return_nullary_function(return_value=0): def nullary_fn(): return return_value return nullary_fn 

Y ahora:

 >>> min(timeit.repeat(lambda: return_nullary_lambda(1))) 0.24312214995734394 >>> min(timeit.repeat(lambda: return_nullary_function(1))) 0.24894469301216304 

Creo que la pequeña diferencia de tiempo anterior se puede atribuir a la búsqueda de return_nullary_function en return_nullary_function : tenga en cuenta que es muy despreciable.

Conclusión

Las Lambdas son buenas para situaciones informales en las que desea minimizar las líneas de código para hacer un punto singular.

Lambdas es malo para situaciones más formales en las que necesita claridad para los editores de código que vendrán más adelante, especialmente en los casos en que no son triviales.

Sabemos que se supone que debemos dar buenos nombres a nuestros objetos. ¿Cómo podemos hacerlo cuando el objeto no tiene nombre?

Por todas estas razones, prefiero crear funciones con def lugar de con lambda .

Si solo va a asignar la lambda a una variable en el ámbito local, también puede usar def porque es más legible y se puede expandir más fácilmente en el futuro:

 fun = lambda a, b: a ** b # a pointless use of lambda map(fun, someList) 

o

 def fun(a, b): return a ** b # more readable map(fun, someList) 

Un uso para las lambdas que he encontrado … está en los mensajes de depuración.

Como las lambdas pueden evaluarse perezosamente, puede tener un código como este

 log.debug(lambda: "this is my message: %r" % (some_data,)) 

en lugar de posiblemente caro:

 log.debug("this is my message: %r" % (some_data,)) 

que procesa la cadena de formato incluso si la llamada de depuración no produce resultados debido al nivel de registro actual.

Por supuesto, para que funcione como se describe, el módulo de registro en uso debe ser compatible con lambdas como “parámetros perezosos” (como lo hace mi módulo de registro).

La misma idea se puede aplicar a cualquier otro caso de evaluación perezosa para la creación de valor de contenido a pedido.

Por ejemplo este operador ternario personalizado:

 def mif(condition, when_true, when_false): if condition: return when_true() else: return when_false() mif(a < b, lambda: a + a, lambda: b + b) 

en lugar de:

 def mif(condition, when_true, when_false): if condition: return when_true else: return when_false mif(a < b, a + a, b + b) 

con lambdas solo se evaluará la expresión seleccionada por la condición, sin lambdas se evaluarán ambas.

Por supuesto, simplemente podría usar funciones en lugar de lambdas, pero para expresiones cortas las lambdas son (c) más magras.

Estoy de acuerdo con Nosklo. Por cierto, incluso si se usa una vez, se desecha la función, la mayoría de las veces solo se desea utilizar algo del módulo del operador.

P.EJ :

Tiene una función con esta firma: myFunction (datos, función de callback).

Quieres pasar una función que agregue 2 elementos.

Usando lambda:

 myFunction(data, (lambda x, y : x + y)) 

La forma pythonica:

 import operator myFunction(data, operator.add) 

O por supuesto, este es un ejemplo simple, pero hay muchas cosas que proporciona el módulo del operador, incluidos los elementos que configuran / obtienen para la lista y dictado. Realmente genial.

  • Tiempo de computación.
  • Función sin nombre.
  • Para lograr una función y muchas funciones de uso.

Considerando un ejemplo simple,

 # CREATE ONE FUNCTION AND USE IT TO PERFORM MANY OPERATIONS ON SAME TYPE OF DATA STRUCTURE. def variousUse(a,b=lambda x:x[0]): return [b(i) for i in a] dummyList = [(0,1,2,3),(4,5,6,7),(78,45,23,43)] variousUse(dummyList) # extract first element variousUse(dummyList,lambda x:[x[0],x[2],x[3]]) # extract specific indexed element variousUse(dummyList,lambda x:x[0]+x[2]) # add specific elements variousUse(dummyList,lambda x:x[0]*x[2]) # multiply specific elements 

Lambda es útil para generar nuevas funciones:

 def somefunc(x): return lambda y: x+y f = somefunc(10) f(2) >>> 12 f(4) >>> 14 

Una diferencia importante es que no se pueden usar las funciones def línea, que en mi opinión es el caso de uso más conveniente para una función lambda . Por ejemplo, al ordenar una lista de objetos:

 my_list.sort(key=lambda o: ox) 

Por lo tanto, sugiero mantener el uso de lambdas para este tipo de operaciones triviales, que tampoco se benefician realmente de la documentación automática que se proporciona al nombrar la función.