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

StackOverflow https://stackoverflow.com/questions/134626

Вопрос

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

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

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

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

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

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

>>> b(5)
6

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

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

“Должен быть один — и желательно только один —очевидный способ сделать это”..

Это было полезно?

Решение

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

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

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

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

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

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

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

Другие советы

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

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

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

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

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

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

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__.Но обычно в этом случае вам лучше использовать функцию.

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

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

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

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

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

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

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

lambda и def являются одними и теми же.

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

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

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

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

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

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

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

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

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

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

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

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

Лямбды не содержат аннотаций и 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

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

Мы используем 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.

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

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

# 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, потому что оно более читабельно и может быть легче расширено в будущем:

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)

Одно из применений для лямбд, которое я нашел...находится в отладочных сообщениях.

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

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

вместо того, чтобы быть возможно дорогим:

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

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

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

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

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

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)

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

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

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

Например, :

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

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

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

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

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

import operator
myFunction(data, operator.add)

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top