Більш бажано: лямбда-функції або вкладені функції ( 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]))