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


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

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

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

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

вложенные функции

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

>>> b(5)
6

есть ли преимущества в использовании одного над другим? (Спектакль? Читаемость? Ограничения? Консистенция? так далее.)

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

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

16 78

16 ответов:

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

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

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

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

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

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

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

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

первая-о том, что они делают и что они возвращаются:

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

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

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

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

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

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

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

  • функция, определенная с помощью 'def', может содержать любую python code
  • функция, определенная с помощью 'lambda', должна вычислять выражение и поэтому не может содержать такие операторы, как print, import, raise,...

например,

def p(x): print x

работает, как ожидалось, в то время как

lambda x: print x

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

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

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

"вопрос: в чем особенность языка Python, вы хоть довольны?

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

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

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

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

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

для 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

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

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

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

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


читаемость:

лямбда-функции несколько менее удобочитаемы для большинства пользователей 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. Они функционально эквивалентны map и filter функции:

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

lambda и def то же самое.

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

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

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

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

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

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

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

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

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

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

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

одно использование для лямбд я нашел... находится в отладочных сообщениях.

поскольку лямбды могут быть лениво оценены, вы можете иметь такой код:

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

вместо дорогого:

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

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

конечно, чтобы он работал так, как описано, используемый модуль ведения журнала должен поддерживать лямбды как "ленивые параметры" (как мой модуль ведения журнала делает.)

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

например, этот пользовательский тернарный оператор:

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)

вместо:

def mif(condition, when_true, when_false):
    if condition:
         return when_true
    else:
         return when_false

mif(a < b, a + a, b + b)

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

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

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

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

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

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

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

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

как лямбды, так и функции:

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

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

A Ламба это __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>

лямбды нет аннотаций и нет docstring

в основном, лямбды не документированы. Давайте перепишем 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

но, вероятно, какая-то ошибка испортила вывод справки.

лямбда-выражения могут только верните выражение

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

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

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

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

в только upside for lambdas: может быть создан в одном выражении

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

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

однако, поскольку 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.

Если вы просто собираетесь назначить лямбду переменной в локальной области, вы можете также использовать def, потому что он более читаем и может быть расширен более легко в будущем:

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

или

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

Я согласен с nosklo. Кстати, даже с использовать один раз, выбросить

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

рассматривая простой пример,

# 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

лямбда полезна для генерации новых функций:

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

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