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