¿Qué funcionalidad functools.partial
ofrece que no puedes obtener a través de lambdas?
No mucho en términos de funcionalidad adicional (pero, ver más adelante), y la legibilidad está en el ojo del espectador.
Parece que a la mayoría de las personas que están familiarizadas con los lenguajes de programación funcionales (en particular las de las familias Lisp / Scheme) les gusta lambda
bien: digo "la mayoría", definitivamente no todas, porque Guido y yo seguramente estamos entre los "familiarizados" (etc. ) Sin embargo, piensa que lambda
es una anomalía monstruosa en Python ...
Se arrepintió de haberlo aceptado en Python, mientras que planeaba eliminarlo de Python 3, como uno de los "problemas técnicos de Python".
Lo apoyé completamente en eso. (Me encanta lambda
en Scheme ... mientras que sus limitaciones en Python , y la forma extraña en que simplemente no con el resto del lenguaje, haz que mi piel se arrastre).
Sin embargo, no es así para las hordas de lambda
amantes, que organizaron una de las cosas más cercanas a una rebelión jamás vista en la historia de Python, hasta que Guido retrocedió y decidió irse lambda
.
Varias adiciones posibles a functools
(para hacer que las funciones regresen constantes, identidad, etc) no sucedió (para evitar duplicar explícitamente más lambda
funcionalidades), aunque partial
por supuesto permaneció (no es una duplicación total , ni es una monstruosidad).
Recuerde que lambda
el cuerpo está limitado a ser una expresión , por lo que tiene limitaciones. Por ejemplo...:
>>> import functools
>>> f = functools.partial(int, base=2)
>>> f.args
()
>>> f.func
<type 'int'>
>>> f.keywords
{'base': 2}
>>>
functools.partial
La función devuelta está decorada con atributos útiles para la introspección: la función que está ajustando y qué argumentos posicionales y nombrados fija allí. Además, los argumentos nombrados pueden anularse de inmediato (la "fijación" es, en cierto sentido, la configuración de los valores predeterminados):
>>> f('23', base=10)
23
Entonces, como ves, ¡ definitivamente no es tan simplista como lambda s: int(s, base=2)
! -)
Sí, podría contorsionar su lambda para darle algo de esto, por ejemplo, para la anulación de palabras clave,
>>> f = lambda s, **k: int(s, **dict({'base': 2}, **k))
¡Pero espero que incluso el lambda
amante más ardiente no considere este horror más legible que la partial
llamada! -). La parte de "configuración de atributos" es aún más difícil, debido a la limitación de "cuerpo de una sola expresión" de Python lambda
(más el hecho de que la asignación nunca puede ser parte de una expresión de Python) ... terminas "falsificando tareas dentro de una expresión" estirando la comprensión de la lista mucho más allá de sus límites de diseño ...:
>>> f = [f for f in (lambda f: int(s, base=2),)
if setattr(f, 'keywords', {'base': 2}) is None][0]
¡Ahora combine la sobreescritura de argumentos nombrados, más la configuración de tres atributos, en una sola expresión, y dígame cuán legible será eso ...!
functools.partial
que mencionaste lo hace superior a lambda. Tal vez este es el tema de otra publicación, pero ¿qué es lo que te molesta tanto a nivel de diseñolambda
?def
ylambda
palabras clave: hacerlos a ambosfunction
(una opción de nombre Javascript realmente correcto), y al menos 1/3 de mis objeciones desaparecerían ! -). Como dije, no tengo ninguna objeción a lambda en Lisp ...! -)def
todos modos. ¡Nuestro benevolente líder ha hablado!Bueno, aquí hay un ejemplo que muestra una diferencia:
Estas publicaciones de Ivan Moore amplían las "limitaciones de lambda" y los cierres en python:
fuente
lambda y, n=n: ...
. La vinculación tardía (de nombres que aparecen solo en el cuerpo de una función, no en sudef
equivalente o equivalentelambda
) no es más que un error, como he mostrado extensamente en respuestas SO largas en el pasado: se vincula explícitamente cuando eso es lo que quiere, use el valor predeterminado de enlace tardío cuando eso es lo que desea, y esa es exactamente la elección de diseño correcta dado el contexto del resto del diseño de Python.En las últimas versiones de Python (> = 2.7), puede
pickle
unpartial
, pero no unlambda
:fuente
multiprocessing.Pool.map()
. stackoverflow.com/a/3637905/195139partial
se puede elegir en Python 2.7.Como respuesta parcial a esto, decidí probar el rendimiento. Aquí está mi ejemplo:
en Python 3.3 da:
Lo que significa que parcial necesita un poco más de tiempo para la creación pero considerablemente menos tiempo para la ejecución. Este bien puede ser el efecto de la unión temprana y tardía que se discute en la respuesta de ars .
fuente
partial
está escrito en C, en lugar de Python puro, lo que significa que puede producir un llamado más eficiente que simplemente crear una función que llame a otra función.Además de la funcionalidad adicional que Alex mencionó, otra ventaja de functools.partial es la velocidad. Con parcial, puede evitar construir (y destruir) otro marco de pila.
Ni la función generada por parcial ni lambdas tienen cadenas de documentos por defecto (aunque puede establecer la cadena de documentos para cualquier objeto a través de
__doc__
).Puede encontrar más detalles en este blog: Aplicación de función parcial en Python
fuente
Entiendo la intención más rápida en el tercer ejemplo.
Cuando analizo lambdas, espero más complejidad / rareza que la que ofrece la biblioteca estándar directamente.
Además, notará que el tercer ejemplo es el único que no depende de la firma completa de
sum2
; haciéndolo un poco más flojo.fuente
functools.partial
llamada, mientras que las lambdas son evidentes.