Что предпочтительнее использовать в Python: лямбда-функции или вложенные функции ('def')? - PullRequest
87 голосов
/ 25 сентября 2008

Я в основном использую лямбда-функции, но иногда использую вложенные функции, которые, кажется, обеспечивают такое же поведение.

Вот несколько тривиальных примеров, когда они функционально делают то же самое, если они были найдены в другой функции:

Лямбда-функция

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

Вложенная функция

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

>>> b(5)
6

Есть ли преимущества использования одного над другим? (Производительность? Читабельность? Ограничения? Согласованность? И т. Д.)

Это вообще имеет значение? Если это не так, это нарушает принцип Питона:

«Должен быть один - и желательно только один - очевидный способ сделать это» .

Ответы [ 16 ]

91 голосов
/ 25 сентября 2008

Если вам нужно присвоить lambda имени, используйте вместо него def. def s - просто синтаксический сахар для задания, поэтому результат тот же, и они намного более гибкие и удобочитаемые.

lambda s можно использовать для использования один раз, отбрасывать функций, которые не будут иметь названия.

Однако этот вариант использования очень редкий. Вам редко требуется передавать безымянные функциональные объекты.

Встроенные функции map() и filter() нуждаются в объектах функций, но списочные выражения и выражения генератора обычно более читабельны, чем эти функции, и могут охватывать все варианты использования без потребность в лямбдах.

Для случаев, когда вам действительно нужен маленький функциональный объект, вы должны использовать функции модуля operator, например operator.add вместо lambda x, y: x + y

Если вам все еще нужны какие-то lambda, не охваченные, вы можете написать def, просто чтобы быть более читабельным. Если функция более сложная, чем в модуле operator, то def, вероятно, лучше.

Итак, в реальном мире хорошие lambda случаи использования очень редки.

28 голосов
/ 26 сентября 2008

Практически, для меня есть два различия:

Первое о том, что они делают и что возвращают:

  • def - это ключевое слово, которое ничего не возвращает и создает «имя» в локальном пространстве имен.

  • lambda - это ключевое слово, которое возвращает объект функции и не создает «имя» в локальном пространстве имен.

Следовательно, если вам нужно вызвать функцию, которая принимает объект функции, единственный способ сделать это в одной строке кода Python - использовать лямбду. Там нет эквивалента с def.

В некоторых рамках это на самом деле довольно часто; например, я часто использую Twisted и делаю что-то вроде

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

довольно часто встречается и более лаконично с лямбдами.

Второе отличие заключается в том, что разрешено делать фактической функции.

  • Функция, определенная с помощью 'def', может содержать любой код Python
  • Функция, определенная с помощью 'lambda', должна вычислять выражение и, следовательно, не может содержать такие выражения, как печать, импорт, повышение, ...

Например,

def p(x): print x

работает как положено, а

lambda x: print x

является ошибкой синтаксиса.

Конечно, есть обходные пути - замените print на sys.stdout.write или import на __import__. Но, как правило, в этом случае лучше использовать функцию.

20 голосов
/ 25 сентября 2008

В этом интервью Гвидо ван Россум говорит, что хотел бы, чтобы он не впустил лямбду в Python:

" В. Какая особенность Python вас меньше всего устраивает?

Иногда я слишком быстро принимал вклады, а потом понял, что это ошибка. Одним из примеров могут быть некоторые функции функционального программирования, такие как лямбда-функции. лямбда - это ключевое слово, которое позволяет вам создать небольшую анонимную функцию; встроенные функции, такие как сопоставление, фильтрация и уменьшение, запускают функцию над типом последовательности, например списком.

На практике это оказалось не так хорошо. Python имеет только две области: локальную и глобальную. Это делает написание лямбда-функций болезненным, потому что вам часто требуется доступ к переменным в области, где была определена лямбда, но вы не можете из-за этих двух областей. Есть способ обойти это, но это что-то вроде клуджа. Зачастую в Python гораздо проще просто использовать цикл for, а не возиться с лямбда-функциями. карта и друзья работают хорошо только тогда, когда уже есть встроенная функция, которая делает то, что вы хотите.

ИМХО, иногда Iambdas могут быть удобны, но обычно удобны за счет читабельности. Можете ли вы сказать мне, что это делает:

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

Я написал это, и мне потребовалась минута, чтобы понять это. Это из Project Euler - я не буду говорить, какая проблема, потому что я ненавижу спойлеры, но он работает за 0,124 секунды:)

9 голосов
/ 16 августа 2012

Для n = 1000 вот несколько раз, когда вызывается функция против лямбды:

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
6 голосов
/ 25 сентября 2008

Я согласен с советом nosklo: если вам нужно дать имя функции, используйте def. Я резервирую lambda функции для случаев, когда я просто передаю краткий фрагмент кода другой функции, например ::10000

a = [ (1,2), (3,4), (5,6) ]
b = map( lambda x: x[0]+x[1], a )
5 голосов
/ 11 февраля 2016

Производительность:

Создание функции с помощью lambda на немного быстрее , чем создание с помощью def. Разница заключается в def создании записи имени в таблице локальных пользователей. Полученная функция имеет одинаковую скорость выполнения.


Дискретность:

Лямбда-функции несколько менее читабельны для большинства пользователей Python, но в некоторых случаях гораздо более кратки. Рассмотрим переход от использования нефункциональной к функциональной рутине:

# Using non-functional version.

heading(math.sqrt(v.x * v.x + v.y * v.y), math.atan(v.y / v.x))

# Using lambda with functional version.

fheading(v, lambda v: math.sqrt(v.x * v.x + v.y * v.y), lambda v: math.atan(v.y / v.x))

# Using def with functional version.

def size(v):
    return math.sqrt(v.x * v.x + v.y * v.y)

def direction(v):
    return math.atan(v.y / v.x)

deal_with_headings(v, size, direction)

Как видите, версия lambda короче и "проще" в том смысле, что вам нужно всего лишь добавить lambda v: к исходной нефункциональной версии для преобразования в функциональную версию. Это также намного более кратко. Но помните, что многие пользователи Python будут смущены лямбда-синтаксисом, поэтому то, что вы потеряете в длине и реальной сложности, может быть возвращено в замешательство коллегам-программистам.


Ограничения:

  • lambda функции могут использоваться только один раз, если они не присвоены имени переменной.
  • lambda функции, назначенные именам переменных, не имеют преимуществ перед def функциями.
  • lambda функции могут быть трудными или невозможными для засоления.
  • def имена функций должны быть тщательно подобраны, чтобы они были достаточно наглядными и уникальными или, по крайней мере, не использовались по объему.

Консистенция:

Python в основном избегает соглашений по функциональному программированию в пользу процедурной и более простой объектной семантики. Оператор lambda прямо противоположен этому смещению. Более того, в качестве альтернативы уже распространенному def, функция lambda добавляет разнообразие вашему синтаксису. Некоторые считают это менее последовательным.


Предварительно существующие функции:

Как отмечалось другими, многие варианты использования lambda в полевых условиях могут быть заменены членами operator или другими модулями. Например:

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

Использование ранее существующей функции может сделать код более читабельным во многих случаях.


Пифоновый принцип: «Должен быть один - и желательно только один - очевидный способ сделать это»

Это похоже на единственный источник истины доктрина. К сожалению, принцип единственного очевидного способа сделать это всегда был скорее задумчивым стремлением к Python, чем истинным руководящим принципом. Рассмотрим очень мощные массивы в Python. Они функционально эквивалентны функциям map и filter:

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

lambda и def одинаковы.

Это вопрос мнения, но я бы сказал, что все в языке Python, предназначенное для общего использования, которое явно ничего не нарушает, достаточно "питонское".

4 голосов
/ 27 декабря 2013

Соглашаясь с другими ответами, иногда это более читабельно. Вот пример, где lambda пригодится, в случае использования я продолжаю сталкиваться с N-мерным defaultdict.
Вот пример:

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

Я считаю его более читабельным, чем создание def для второго измерения. Это еще более важно для более высоких измерений.

3 голосов
/ 27 марта 2018

Более предпочтительно: лямбда-функции или вложенные функции (def)?

Существует одно преимущество использования лямбды над обычной функцией (они создаются в выражении) и несколько недостатков. По этой причине я предпочитаю создавать функции с ключевым словом def, а не с лямбдами.

Первая точка - это один и тот же тип объекта

Лямбда приводит к тому же типу объекта, что и обычная функция

>>> l = lambda: 0
>>> type(l)
<class 'function'>
>>> def foo(): return 0
... 
>>> type(foo)
<class 'function'>
>>> type(foo) is type(l)
True

Поскольку лямбда-выражения являются функциями, они являются объектами первого класса.

Обе лямбды и функции:

  • может передаваться как аргумент (так же, как обычная функция)
  • при создании внутри внешней функции становится замыканием над локальными элементами этой внешней функции

Но в лямбдах по умолчанию отсутствуют некоторые вещи, которые функции получают через полный синтаксис определения функции.

Ламба __name__ это '<lambda>'

В конце концов, лямбды - это анонимные функции, поэтому они не знают своего имени.

>>> l.__name__
'<lambda>'
>>> foo.__name__
'foo'

Таким образом, лямбды нельзя программно искать в их пространстве имен.

Это ограничивает определенные вещи. Например, foo можно искать с помощью сериализованного кода, а l нельзя:

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

Мы можем просто найти foo, потому что он знает свое имя:

>>> pickle.loads(pickle.dumps(foo))
<function foo at 0x7fbbbee79268>

В лямбдах нет аннотаций и строк документации

По сути, лямбды не задокументированы. Давайте перепишем foo, чтобы лучше документировать:

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

Теперь у foo есть документация:

>>> foo.__annotations__
{'return': <class 'int'>}
>>> help(foo)
Help on function foo in module __main__:

foo() -> int
    a nullary function, returns 0 every time

Принимая во внимание, что у нас нет одного и того же механизма для предоставления одной и той же информации лямбдам:

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

<lambda> lambda (...)

Но мы можем взломать их:

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

<lambda> lambda ) -> in
    nullary -> 0

Но, возможно, из-за ошибки выводится справка.

Lambdas может возвращать только выражение

Lambdas не может возвращать сложные выражения, только выражения.

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

Выражения могут быть довольно сложными, и если вы попробуете очень сильно, вы, вероятно, можете сделать то же самое с лямбда-выражением, но добавленная сложность - больше вреда написанию понятного кода.

Мы используем Python для ясности и удобства обслуживания. Злоупотребление лямбдами может сработать против этого.

только верх для лямбд: может быть создано в одном выражении

Это единственный возможный потенциал. Поскольку вы можете создать лямбда-выражение с выражением, вы можете создать его внутри вызова функции.

Создание функции внутри вызова функции позволяет избежать (недорогого) поиска имени по сравнению с созданной в другом месте.

Тем не менее, поскольку Python строго оценен, нет никакого другого выигрыша в производительности, кроме как избегать поиска имени.

Для очень простого выражения я мог бы выбрать лямбду.

Я также склонен использовать лямбды при работе с интерактивным Python, чтобы избежать нескольких строк, когда одна будет делать. Я использую следующий вид формата кода, когда я хочу передать аргумент конструктору при вызове 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

А теперь:

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

Я полагаю, что небольшая разница во времени, приведенная выше, может быть связана с поиском имени в return_nullary_function - обратите внимание, что очень незначительно.

Заключение

Лямбды хороши для неформальных ситуаций, когда вы хотите свести к минимуму строки кода в пользу создания особой точки.

Лямбды плохи для более формальных ситуаций, когда вам нужна ясность для редакторов кода, которые появятся позже, особенно в тех случаях, когда они нетривиальны.

Мы знаем, что должны давать нашим объектам хорошие имена. Как мы можем сделать это, когда объект имеет нет имя?

По всем этим причинам я предпочитаю создавать функции с def вместо lambda.

3 голосов
/ 17 июня 2009

Важным ограничением лямбд является то, что они не могут содержать ничего, кроме выражения. Для лямбда-выражения почти невозможно произвести что-либо, кроме тривиальных побочных эффектов, поскольку оно не может иметь такого богатого тела, как def 'ed функция.

При этом, Lua повлиял на мой стиль программирования в сторону широкого использования анонимных функций, и я засоряю их кодом. Вдобавок ко всему, я склонен думать о преобразовании / сокращении как об абстрактных операторах способами, которые я не рассматриваю в списках или генераторах, почти как если бы я явно откладывал решение о реализации с помощью этих операторов.

Редактировать: Это довольно старый вопрос, и мои мнения по этому вопросу несколько изменились.

Во-первых, я решительно настроен против присвоения выражения lambda переменной; поскольку у питона есть специальный синтаксис только для этого (подсказка, def). В дополнение к этому, многие из применений лямбды, даже когда они не получают имя, имеют предопределенные (и более эффективные) реализации. Например, рассматриваемый пример может быть сокращен до просто (1).__add__, без необходимости заключать его в lambda или def. Многие другие общие применения могут быть удовлетворены некоторой комбинацией модулей operator, itertools и functools.

3 голосов
/ 25 сентября 2008

Основное использование лямбды всегда было для простых функций обратного вызова, а также для отображения, сокращения, фильтрации, для которых требуется функция в качестве аргумента. С пониманием списка становится нормой, и добавление разрешено, если как в:

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

Трудно представить реальный случай использования лямбды в повседневном использовании. В результате, я бы сказал, избегайте лямбды и создавайте вложенные функции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...