Какие функции functools.partial
вы не можете получить с помощью лямбда-выражений?
Немногое с точки зрения дополнительной функциональности (но, см. Ниже) - и читаемость в глазах смотрящего.
Большинству людей, знакомых с функциональными языками программирования (в частности, из семейств Lisp / Scheme), кажется, это нравится lambda
просто - я говорю «большинство», определенно не все, потому что мы с Гвидо, несомненно, относимся к тем, кто «знаком с» (т. ), но считают, lambda
что это неприятная аномалия в Python ...
Он раскаивался в том, что когда-либо принимал это в Python, тогда как планировал удалить его из Python 3, как один из «глюков Python».
Я полностью его в этом поддержал. (Мне нравится lambda
Scheme ... в то время как его ограничения в Python и странный способ, которым он просто не с остальным языком заставляю кожу ползать).
Однако не так для орды lambda
влюбленных, которые устроили один из самых близких к восстанию событий в истории Python, пока Гвидо не отступил и не решил уйти lambda
.
Несколько возможных дополнений к functools
(чтобы сделать функции, возвращающие константы, идентичность, и т.д.) не произошло (чтобы избежать явного дублирования дополнительных lambda
функций), хотя partial
, конечно, осталось (это не полное дублирование и не бельмо на глазу).
Помните, что lambda
тело ограничено выражением , поэтому у него есть ограничения. Например...:
>>> import functools
>>> f = functools.partial(int, base=2)
>>> f.args
()
>>> f.func
<type 'int'>
>>> f.keywords
{'base': 2}
>>>
functools.partial
Возвращаемая функция украшена атрибутами, полезными для самоанализа - функцией, которую она обертывает, и позиционными и именованными аргументами, которые она там исправляет. Кроме того, названные аргументы могут быть переопределены сразу же («исправление», скорее, в некотором смысле, установка значений по умолчанию):
>>> f('23', base=10)
23
Так что, как видите, это определенно не так упрощенно, как lambda s: int(s, base=2)
! -)
Да, вы можете изменить свою лямбду, чтобы дать вам кое-что из этого - например, для переопределения ключевых слов,
>>> f = lambda s, **k: int(s, **dict({'base': 2}, **k))
но очень надеюсь, что даже самый ярый lambda
любитель не сочтет этот ужас более читабельным, чем partial
призыв! -). Часть «установки атрибута» еще сложнее из-за ограничения Python «тело - одно выражение» lambda
(плюс тот факт, что присваивание никогда не может быть частью выражения Python) ... вы в конечном итоге «подделываете присваивания внутри выражения» расширяя понимание списка далеко за пределы его дизайна ...:
>>> f = [f for f in (lambda f: int(s, base=2),)
if setattr(f, 'keywords', {'base': 2}) is None][0]
Теперь объединить названные-аргументы overridability, а также установку трех атрибутов, в одно выражение, и скажите мне, насколько читаемым , что будет ...!
functools.partial
которых вы упомянули, делают его лучше лямбда. Возможно, это тема другого поста, но что вас так сильно беспокоит на уровне дизайнаlambda
?def
иlambda
ключевые слова: сделайте их и то и другоеfunction
(выбор одного имени Javascript был действительно правильным), и по крайней мере 1/3 моих возражений исчезнет ! -). Как я уже сказал, я не возражаю против лямбда в Лиспе ...! -)def
равно можете использовать . Наш доброжелательный лидер заговорил!Что ж, вот пример, который показывает разницу:
In [132]: sum = lambda x, y: x + y In [133]: n = 5 In [134]: incr = lambda y: sum(n, y) In [135]: incr2 = partial(sum, n) In [136]: print incr(3), incr2(3) 8 8 In [137]: n = 9 In [138]: print incr(3), incr2(3) 12 8
Эти сообщения Ивана Мура расширяют «ограничения лямбда» и замыкания в Python:
источник
lambda y, n=n: ...
. Позднее связывание (имена , появляющихся только в теле функции, а не в егоdef
или эквивалентеlambda
) ничего , но ошибка, как я уже показал, наконец , в длинных С.О. ответах в прошлом: вы рано связывать явно , когда это то, что вы хотите, используйте значение по умолчанию с поздним связыванием, если это то, что вы хотите, и это как раз правильный выбор дизайна, учитывая контекст остальной части дизайна Python.В последних версиях Python (> = 2.7), вы можете , но не :
pickle
partial
lambda
>>> pickle.dumps(partial(int)) 'cfunctools\npartial\np0\n(c__builtin__\nint\np1\ntp2\nRp3\n(g1\n(tNNtp4\nb.' >>> pickle.dumps(lambda x: int(x)) Traceback (most recent call last): File "<ipython-input-11-e32d5a050739>", line 1, in <module> pickle.dumps(lambda x: int(x)) File "/usr/lib/python2.7/pickle.py", line 1374, in dumps Pickler(file, protocol).dump(obj) File "/usr/lib/python2.7/pickle.py", line 224, in dump self.save(obj) File "/usr/lib/python2.7/pickle.py", line 286, in save f(self, obj) # Call unbound method with explicit self File "/usr/lib/python2.7/pickle.py", line 748, in save_global (obj, module, name)) PicklingError: Can't pickle <function <lambda> at 0x1729aa0>: it's not found as __main__.<lambda>
источник
multiprocessing.Pool.map()
. stackoverflow.com/a/3637905/195139partial
написан в 2010 году. Его можно редактировать в Python 2.7.В качестве частичного ответа на это я решил проверить производительность. Вот мой пример:
from functools import partial import time, math def make_lambda(): x = 1.3 return lambda: math.sin(x) def make_partial(): x = 1.3 return partial(math.sin, x) Iter = 10**7 start = time.clock() for i in range(0, Iter): l = make_lambda() stop = time.clock() print('lambda creation time {}'.format(stop - start)) start = time.clock() for i in range(0, Iter): l() stop = time.clock() print('lambda execution time {}'.format(stop - start)) start = time.clock() for i in range(0, Iter): p = make_partial() stop = time.clock() print('partial creation time {}'.format(stop - start)) start = time.clock() for i in range(0, Iter): p() stop = time.clock() print('partial execution time {}'.format(stop - start))
на Python 3.3 это дает:
Это означает, что частичному требуется немного больше времени для создания, но значительно меньше времени для выполнения. Это вполне может быть результатом раннего и позднего связывания, которые обсуждаются в ответе от ars .
источник
partial
он написан на C, а не на чистом Python, что означает, что он может создавать более эффективный вызываемый объект, чем просто создание функции, которая вызывает другую функцию.Помимо дополнительных функций, упомянутых Алексом, еще одним преимуществом functools.partial является скорость. С partial вы можете избежать создания (и разрушения) другого кадра стека.
Ни функция, сгенерированная частичным, ни лямбда-выражения по умолчанию не имеют строк документации (хотя вы можете установить строку документа для любых объектов через
__doc__
).Вы можете найти более подробную информацию в этом блоге: Приложение с частичной функцией в Python
источник
Я быстрее всего понимаю намерение в третьем примере.
Когда я разбираю лямбды, я ожидаю большей сложности / странности, чем это предлагается непосредственно стандартной библиотекой.
Также вы заметите, что третий пример - единственный, который не зависит от полной подписи
sum2
; что делает его немного более слабым.источник
functools.partial
вызов, тогда как лямбды очевидны.