Предпочтительнее: лямбда-функции или вложенные функции ( def
)?
У использования лямбда-выражений перед обычной функцией есть одно преимущество: они создаются в выражении.
Есть несколько недостатков:
- без имени (просто
'<lambda>'
)
- нет документации
- без аннотаций
- никаких сложных заявлений
Они также являются объектами одного типа. По этим причинам я обычно предпочитаю создавать функции с 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
.
lambda
, но я не согласен с тем, что это «очень редко», обычно для ключевых функцийsorted
иitertools.groupby
т. Д., Напримерsorted(['a1', 'b0'], key= lambda x: int(x[1]))