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

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

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

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

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

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

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

>>> b(5)
6

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

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

There should be one—and preferably only one—obvious way to do it.

Ответ 1

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

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

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

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

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

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

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

Ответ 2

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

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

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

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

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

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

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

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

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

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

Например,

def p(x): print x

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

lambda x: print x

- это синтаксис.

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

Ответ 3

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

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

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

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

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

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

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

Ответ 4

При n = 1000 здесь некоторое время вызова функции vs lambda:

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

Ответ 5

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

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

Ответ 6

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

Создание функции с помощью 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, а не истинным руководящим руководителем. Рассмотрим очень мощные методы построения массивов в Python. Они функционально эквивалентны функциям map и filter:

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

lambda и def совпадают.

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

Ответ 7

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

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

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

Ответ 8

  Более предпочтительно: лямбда-функции или вложенные функции (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

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

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

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

>>> 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.

Ответ 9

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

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

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

Ответ 10

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

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

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

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

Ответ 11

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

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

# 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

Ответ 12

Если вы просто собираетесь присвоить лямбда переменной в локальной области, вы можете также использовать 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)

Ответ 13

Одно использование для lambdas, которое я нашел... находится в сообщениях отладки.

Поскольку lambdas можно лениво оценить, вы можете иметь такой код:

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

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

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

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

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

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

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

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)

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

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

Ответ 14

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

E.G:

У вас есть функция с этой сигнатурой: myFunction (данные, функция обратного вызова).

Вы хотите передать функцию, добавляющую 2 элемента.

Использование лямбда:

myFunction(data, (lambda x, y : x + y))

Питонический путь:

import operator
myFunction(data, operator.add)

Или, конечно, это простой пример, но есть много всего, что предоставляет операторский модуль, в том числе сеттеры/геттеры для списка и dict. Действительно здорово.

Ответ 15

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

my_list.sort(key=lambda o: o.x)

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

Ответ 16

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

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