Quelle fonctionnalité functools.partial
offre que vous ne pouvez pas obtenir avec lambdas?
Pas beaucoup en termes de fonctionnalités supplémentaires (mais, voir plus loin) - et la lisibilité est dans l'œil du spectateur.
La plupart des gens qui connaissent les langages de programmation fonctionnels (ceux des familles Lisp / Scheme en particulier) semblent aimer lambda
très bien - je dis "la plupart", certainement pas tous, parce que Guido et moi sommes assurément parmi ceux qui "sont familiers" (etc.) ) mais pense lambda
à une anomalie horrible en Python ...
Il se repentait de ne jamais l'avoir acceptée en Python alors qu'il prévoyait de le supprimer de Python 3, comme l'un des "pépins de Python".
Je l'ai pleinement soutenu à cet égard. (J'adore lambda
dans Scheme ... tandis que ses limitations en Python , et la façon étrange qu'il ne fait pas avec le reste du langage, font ramper ma peau).
Ce n'est pas le cas, cependant, pour les hordes d' lambda
amoureux - qui ont organisé l'une des choses les plus proches d'une rébellion jamais vue dans l'histoire de Python, jusqu'à ce que Guido revienne en arrière et décide de partir lambda
.
Plusieurs ajouts possibles à functools
(pour rendre les fonctions renvoyant des constantes, l'identité, etc) ne s'est pas produit (pour éviter de dupliquer explicitement plus de lambda
fonctionnalités), bien partial
qu'il soit bien sûr resté (ce n'est pas une duplication totale , ni une horreur).
N'oubliez pas que lambda
le corps est limité à être une expression , il a donc des limites. Par exemple...:
>>> import functools
>>> f = functools.partial(int, base=2)
>>> f.args
()
>>> f.func
<type 'int'>
>>> f.keywords
{'base': 2}
>>>
functools.partial
La fonction renvoyée est décorée d'attributs utiles pour l'introspection - la fonction qu'elle encapsule et les arguments positionnels et nommés qu'elle y corrige. De plus, les arguments nommés peuvent être remplacés tout de suite (la «correction» est plutôt, dans un sens, la définition des valeurs par défaut):
>>> f('23', base=10)
23
Donc, comme vous le voyez, ce n'est certainement pas aussi simpliste que lambda s: int(s, base=2)
! -)
Oui, vous pouvez déformer votre lambda pour vous en fournir une partie - par exemple, pour le mot clé prioritaire,
>>> f = lambda s, **k: int(s, **dict({'base': 2}, **k))
mais j'espèrelambda
sincèrement que même l' amant le plus ardent ne considère pas cette horreur plus lisible que l' partial
appel! -). La partie "définition d'attribut" est encore plus difficile, en raison de la limitation "corps une seule expression" de Python lambda
(plus le fait que l'affectation ne peut jamais faire partie d'une expression Python) ... vous finissez par "simuler des affectations dans une expression" en étirant la compréhension de la liste bien au-delà de ses limites de conception ...:
>>> f = [f for f in (lambda f: int(s, base=2),)
if setattr(f, 'keywords', {'base': 2}) is None][0]
Maintenant, combinez la substituabilité des arguments nommés, plus le réglage de trois attributs, en une seule expression, et dites-moi à quel point cela sera lisible ...!
functools.partial
que vous avez mentionnée la rend supérieure à lambda. C'est peut-être le sujet d'un autre article, mais qu'est-ce qui vous dérange tant au niveau du designlambda
?def
et leslambda
mots - clés: faites-les tous les deuxfunction
(un choix de nom Javascript a vraiment raison), et au moins 1/3 de mes objections disparaîtraient ! -). Comme je l'ai dit, je n'ai aucune objection à lambda à Lisp ...! -)def
toute façon. Notre leader bienveillant s'est exprimé!Eh bien, voici un exemple qui montre une différence:
Ces articles d'Ivan Moore développent les "limitations de lambda" et les fermetures en python:
la source
lambda y, n=n: ...
. La liaison tardive (des noms n'apparaissant que dans le corps d'une fonction, pas dans son corpsdef
ou équivalentlambda
) est tout sauf un bogue, comme je l'ai montré longuement dans les réponses SO longues: vous vous liez tôt explicitement quand c'est ce que vous voulez, utilisez la valeur par défaut de liaison tardive lorsque c'est ce que vous voulez, et c'est exactement le bon choix de conception compte tenu du contexte du reste de la conception de Python.Dans les dernières versions de Python (> = 2.7), vous pouvez
pickle
unpartial
, mais pas unlambda
:la source
multiprocessing.Pool.map()
. stackoverflow.com/a/3637905/195139partial
est pickleable en Python 2.7.En réponse à cela, j'ai décidé de tester les performances. Voici mon exemple:
sur Python 3.3, cela donne:
Ce qui signifie que le partiel a besoin d'un peu plus de temps pour la création mais considérablement moins de temps pour l'exécution. Cela peut bien être l'effet de la liaison précoce et tardive qui est discutée dans la réponse d' ars .
la source
partial
est écrit en C, plutôt qu'en Python pur, ce qui signifie qu'il peut produire un appelable plus efficace que de simplement créer une fonction qui appelle une autre fonction.Outre les fonctionnalités supplémentaires mentionnées par Alex, un autre avantage de functools.partial est la vitesse. Avec partial, vous pouvez éviter de construire (et de détruire) un autre cadre de pile.
Ni la fonction générée par partial ni lambdas n'ont de docstrings par défaut (bien que vous puissiez définir la chaîne de doc pour tous les objets via
__doc__
).Vous pouvez trouver plus de détails dans ce blog: Application de fonction partielle en Python
la source
Je comprends l'intention la plus rapide dans le troisième exemple.
Lorsque j'analyse des lambdas, je m'attends à plus de complexité / bizarrerie que celle offerte par la bibliothèque standard directement.
De plus, vous remarquerez que le troisième exemple est le seul qui ne dépend pas de la signature complète de
sum2
; le rendant ainsi légèrement plus lâche couplé.la source
functools.partial
appel, alors que les lambdas vont de soi.