Чому корисні лямбди Python? [зачинено]


928

Я намагаюся розібратися в лямбдах Python. Чи є лямбда одним із тих "цікавих" мовних предметів, про які в реальному житті слід забути?

Я впевнений, що є певні крайні випадки, коли це може знадобитися, але зважаючи на його неясність, потенціал його буде переосмислений у майбутніх випусках (моє припущення, виходячи з різних його визначень) та зменшена чіткість кодування - якщо це уникати?

Це нагадує мені про переповнення (переповнення буфера) типів C - вказівка ​​на верхню змінну та перевантаження для встановлення інших значень поля. Це здається, що це своєрідне показове мистецтво, але кошмар технічного обслуговування кодера.


261
+1 Добре питання - погані припущення (незрозумілість лямбда) =) Намагайтеся не судити про методи програмування. Оцініть їх і додайте їх до свого розумового набору інструментів. Якщо вони вам не подобаються, не використовуйте їх і будьте готові логічно їх обговорювати, не стаючи релігійними.
Kieveli

41
Правила Haskell! Функції лямбда надають виразності та силі абстракції.
Джонатан Барберо

11
@JAL Не кажучи вже про LISP ...
НаближенняDarknessFish

8
@ApproachingDarknessFish "Ах, це Парентез вашого батька. Більш цивілізована зброя з більш цивілізованого віку". - Оби Лісп Кенобі
Поля

Відповіді:


1008

Ви говорите про лямбда-функції ? Подібно до

lambda x: x**2 + 2*x - 5

Ці речі насправді досить корисні. Python підтримує стиль програмування, який називається функціональним програмуванням, де ви можете передавати функції іншим функціям, щоб виконувати завдання. Приклад:

mult3 = filter(lambda x: x % 3 == 0, [1, 2, 3, 4, 5, 6, 7, 8, 9])

встановлює mult3до [3, 6, 9]тих елементів оригінального списку, які кратні 3. Це коротше (і, можна стверджувати, чіткіше), ніж

def filterfunc(x):
    return x % 3 == 0
mult3 = filter(filterfunc, [1, 2, 3, 4, 5, 6, 7, 8, 9])

Звичайно, у цьому конкретному випадку ви можете зробити те саме, що і з розумінням списку:

mult3 = [x for x in [1, 2, 3, 4, 5, 6, 7, 8, 9] if x % 3 == 0]

(або навіть як range(3,10,3)), але є багато інших, більш досконалих випадків використання, коли ви не можете використовувати розуміння списку, а лямбда-функція може бути найкоротшим способом написати щось.

  • Повернення функції з іншої функції

    >>> def transform(n):
    ...     return lambda x: x + n
    ...
    >>> f = transform(3)
    >>> f(4)
    7

    Це часто використовується для створення функціональних обгортків, таких як декоратори Python.

  • Поєднання елементів ітерабельної послідовності з reduce()

    >>> reduce(lambda a, b: '{}, {}'.format(a, b), [1, 2, 3, 4, 5, 6, 7, 8, 9])
    '1, 2, 3, 4, 5, 6, 7, 8, 9'
  • Сортування за альтернативним ключем

    >>> sorted([1, 2, 3, 4, 5, 6, 7, 8, 9], key=lambda x: abs(5-x))
    [5, 4, 6, 3, 7, 2, 8, 1, 9]

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


26
Любіть приклади, дуже легко зрозуміти. Але для скорочення. Якщо мені доведеться реалізувати цю функцію. Я б робив','.join(str(x) for x in [1,2,3,4,5,6,7,8,9])
etlds

3
BTW, якщо ви запускаєте це на Python3, вам потрібно зателефонувати у списку результатів фільтру, щоб побачити фактичні значення, також вам потрібно імпортувати зменшення з функціональних пристроїв
Джерард

Ми впевнені у визначенні поняття "функціональне програмування" вище? Мене це трохи заплутало.
stdout

3
Я думаю, що ключовим моментом є те, що lambdaфункції можуть бути анонімними (як це є у всіх ваших прикладах). Якщо ви призначаєте якусь lambdaфункцію, то ви робите її неправильно і defзамість цього слід використовувати
Chris_Rands

1
@zgulser Це не визначення, це лише твердження про щось, що дозволяє вам функціональне програмування.
David Z

377

lambdaце просто вигадливий спосіб сказати function. Крім його назви, в цьому немає нічого незрозумілого, залякаючого або кричущого. Коли ви читаєте наступний рядок, замініть lambdaйого functionна думку:

>>> f = lambda x: x + 1
>>> f(3)
4

Він просто визначає функцію x. Деякі інші мови, як-от R, говорять це прямо:

> f = function(x) { x + 1 }
> f(3)
4

Розумієш? Це одна з найприродніших речей, яку потрібно робити в програмуванні.


36
Це чудовий опис для тих, хто походить з непрограмуючих фонів (тобто точних наук), що робить сенс lambda дуже простим для розуміння. Дякую!
Габріель

10
Реймонд Хеттінгер нарікав на ім'я в одному зі своїх розмов і сказав, що всякої плутанини можна було уникнути, назвавши його "виконувати функцію" замість "лямбда". :-)
ankush981

10
замініть lambdaна functionсвою думку і додайте returnдо останнього виразу
Ciprian Tomoiagă

4
@AaronMcMillin Спробуйте type(lambda x: 3). lambdaвирази та defвисловлювання генерують functionоб'єкти; це лише синтаксис lambdaвиразу, який обмежує, які екземпляри можуть створювати.
чепнер

6
@AaronMcMillin Ви не вистачаєте моєї точки зору. Тільки тому, що ви не можете визначити кожну функцію за допомогою lambdaвиразу, не означає, що результат lambdaвираження не є функцією.
чепнер

107

Дворядковий підсумок:

  1. Закриття : Дуже корисно. Вивчіть їх, використовуйте, любите їх.
  2. lambdaКлючове слово Python : непотрібне, періодично корисне. Якщо ви виявите, що ви робите з цим щось складне, віддаліть його та визначте реальну функцію.

72

Лямбда - це частина дуже важливого механізму абстрагування, який стосується функцій вищого порядку. Щоб правильно зрозуміти його значення, будь ласка, перегляньте уроки високої якості від Абельсона та Суссмана та прочитайте книгу SICP

Це актуальні питання сучасного програмного бізнесу та стають все більш популярними.


1
Лямбда-вирази стають популярними і в інших мовах (наприклад, C #). Вони нікуди не йдуть. Читання про закриття було б корисною вправою для розуміння Лямбда. Замикання дозволяють зробити магію кодування можливою в таких структурах, як jQuery.
Дан Еспарза

3
Не плутайте функції lambdas та першокласні. Python має надзвичайно обмежене lambdaтвердження, але повністю першокласні функції. Єдина різниця, яку він має, - це те, що ви повинні назвати функції, які ви хочете пройти.
Гарет Латті

59

лямбда надзвичайно корисні в програмуванні GUI. Наприклад, скажімо, ви створюєте групу кнопок, і ви хочете використовувати один параматеризований зворотний дзвінок, а не унікальний зворотний виклик на кнопку. Лямбда дозволяє вам це легко зробити:

for value in ["one","two","three"]:
    b = tk.Button(label=value, command=lambda arg=value: my_callback(arg))
    b.pack()

(Примітка. Хоча це питання конкретно задаєтьсяlambda , ви також можете використовувати functools.partial для отримання того ж типу результату)

Альтернативою є створення окремого зворотного дзвінка для кожної кнопки, що може призвести до дублювання коду.


1
Саме тому я роздивився, що таке лямбда, але чому це працює, для мене це виглядає точно так само, як просто виклик функції прямо ( stackoverflow.com/questions/3704568/… ). Можливо, пізно, це і працює, але чому це працює?
Rqomey

5
@Rqomey: різниця полягає в тому, що в цьому прикладі valueвизначено цикл; в іншому прикладі параметр завжди мав лише одне значення. Коли ви додаєте щось на кшталт arg=value, ви додаєте поточне значення до зворотного дзвінка. Без цього ви прив'язуєте посилання на змінну в зворотному дзвінку. Посилання завжди буде містити остаточне значення змінної, оскільки зворотний виклик відбувається через деякий час після завершення циклу.
Брайан Оуклі

1
Щойно я працював учора, я, чесно кажучи, не можу повірити, наскільки це корисно ... Я можу створити меню з одного для циклу та CSV-файлу для конфігурації. Дійсно корисні речі.
Rqomey

1
Зауважте, існування functools.partial()якого дозволяє зробити це з меншою сумою (і без lambda).
Гарет Летті

2
partial(my_callback, value)vs lambda arg=value: my_callback(arg)- лямбда має набагато більше суворості (присвоєння аргументу, а потім використання), і менш зрозуміло, який це задум (ви можете робити щось тонко інше в лямбді). Імпорт насправді не є проблемою (у вас все одно є купа, і це один раз на файл). Код найкраще судити про те, наскільки добре він читається, і його читати partial()набагато простіше, ніж лямбда.
Гарет Летті

58

Сумніваюся, лямбда піде. Дивіться публікацію Гідо про остаточну відмову від спроби її видалити. Також дивіться контур конфлікту .

Ви можете ознайомитись з цим дописом, щоб отримати докладнішу інформацію про угоду, яка стоїть за функціональними можливостями Python: http://python-history.blogspot.com/2009/04/origins-of-pythons-functional-features.html

Цікаво, що карти, фільтри та скорочення функцій, які спочатку мотивували введення лямбда та інші функціональні особливості, значною мірою були витіснені з розуміння списків та виразів генератора. Фактично, функція зменшення була видалена зі списку вбудованих функцій в Python 3.0. (Однак, не потрібно надсилати скарги на видалення лямбда, карти чи фільтра: вони залишаються. :-)

Мої два центи: Рідко коштує лямбда, наскільки ясність йде. Як правило, є більш чітке рішення, яке не включає лямбда.


2
зауважте, що зменшення все ще є важливим у Python 3.0. Якщо ви дійсно хочете цього, ви все одно можете його мати.
Paweł Polewicz

Я думаю, що спроба Гвідо стосувалася більше синтаксису. Ця людина також вважає, що: cackhanded.com/blog/post/2008/01/24/…
leewz

38

У Python - lambdaце лише спосіб визначення вбудованих функцій,

a = lambda x: x + 1
print a(1)

і ..

def a(x): return x + 1
print a(1)

.. точно так само.

З лямбдаю нічого не можна зробити, чого не можна зробити за допомогою звичайної функції - в Python функції є об'єктом так само, як і все інше, а лямбди просто визначають функцію:

>>> a = lambda x: x + 1
>>> type(a)
<type 'function'>

Я, чесно кажучи, вважаю, що lambdaключове слово в Python не є надмірним - я ніколи не мав потреби їх використовувати (або бачив таке, яке використовувалося там, де замість нього можна було б краще використовувати звичайну функцію, розуміння списку або одну з багатьох вбудованих функцій)

Для цілком випадкового прикладу зі статті "лямбда Пітона розбита!" :

Щоб побачити, як лямбда розбита, спробуйте створити список функцій, fs=[f0,...,f9]де fi(n)=i+n. Перша спроба:

>>> fs = [(lambda n: i + n) for i in range(10)]
>>> fs[3](4)
13

Я б заперечував, навіть якби це спрацювало, це жахливо і "непітонічно", таку ж функціональність можна було б записати незліченною кількістю інших способів, наприклад:

>>> n = 4
>>> [i + n for i in range(10)]
[4, 5, 6, 7, 8, 9, 10, 11, 12, 13]

Так, це не те саме, але я ніколи не бачив причини, коли генерувати групу лямбда-функцій у списку потрібно. Це може мати сенс в інших мовах, але Python - це не Haskell (або Lisp, або ...)

Зверніть увагу, що ми можемо використовувати лямбда і все-таки досягти бажаних результатів таким чином:

>>> fs = [(lambda n,i=i: i + n) for i in range(10)]
>>> fs[3](4)
7

Редагувати:

Є кілька випадків, коли лямбда корисна, наприклад, це часто зручно під час підключення сигналів у програмах PyQt, наприклад:

w = PyQt4.QtGui.QLineEdit()
w.textChanged.connect(lambda event: dothing())

Просто виконання w.textChanged.connect(dothing)закликає dothingметод із додатковим eventаргументом і спричинить помилку. Використовуючи лямбда означає, що ми можемо охайно відкинути аргумент без необхідності визначати функцію обгортання.


2
ваш аргумент "лямбда зламана" порушений, тому що правила зміщення рівня змінної пітона працюють таким чином, період. Ви будете покусані таким же чином, якщо створили закриття всередині for-loop.
Антті Хаапала

1
лямбда - це лише спосіб python надати користувачеві функцію "анонім", як і багато інших мов (наприклад, javascript).
Стефан Грюенвальд

1
Лямбда та функції a, які ви визначили, не зовсім однакові. :) Вони різняться __name__принаймні за полями ...
nperson325681

1
Це працює більше, ніж просто вбудована функція.
kta

Ваші міркування про те, що "може мати сенс в іншій мові", дивні. Або є внутрішні якості лямбда, або їх немає.
Титу

31

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

Як і множинні правила Mozilla :

plural_rules = [
    lambda n: 'all',
    lambda n: 'singular' if n == 1 else 'plural',
    lambda n: 'singular' if 0 <= n <= 1 else 'plural',
    ...
]
# Call plural rule #1 with argument 4 to find out which sentence form to use.
plural_rule[1](4) # returns 'plural'

Якщо вам доведеться визначити функцію для всіх тих, хто з глузду з'їде до кінця.
Крім того , це не було б добре з іменами функцій , як plural_rule_1, plural_rule_2і т.д. І ви повинні були б eval()це , коли ви в залежності від функції змінної ід.


1
Це схоже на короткі зустрічі, які у мене досі були у F # із узгодженням шаблону та параметрами. Чи маєте ви більше інформації про те, як використовувати цей синтаксис?
Кен

26

Практично все, що ви можете зробити, lambdaви можете зробити краще з або названими функціями, або з переліками та генераторами.

Отже, здебільшого вам слід бути лише одним із тих, хто знаходиться в будь-якій ситуації (за винятком, можливо, коду нуля, написаного в інтерактивному перекладачі).


3
"Здебільшого ти повинен бути лише одним із тих, хто знаходиться в основному в будь-якій ситуації" Період. Введення лямбда в перекладачі навіть не так корисно.
С.Лотт

11
@Javier Я згоден з вами, якщо ви говорите про концепцію "лямбда"; однак, якщо ми говоримо про "лямбда" ключове слово python, тоді: 1) названі функції швидші і можуть робити більше (висловлювання + вирази) майже в будь-якому місці, де ви б використовували лямбда (карта + фільтр), ви можете просто генерувати вирази або списувати розуміння які більш витончені та стислі. Я не кажу, що функції першого класу не круті, лише те, що ключове слово "лямбда" в python не так добре, як просто використання названої функції, це все.
Аарон Маенпаа

3
лямбда мені незамінна для використання з функціями, які беруть аргументи зворотного виклику, такі як ключ = аргумент для сортування () та сортування ()
Рік Коупленд

19
@Rick Я в цьому не сумніваюся, але реальність полягає в тому, що коли ви побачите "лямбда" і ви думаєте "зоммигод лямбда" і почнете писати код схеми в python, ви будете невдоволено розчаровані обмеженнями лямбдаського виразу python. З іншого боку, якщо ви почнете замислюватися над собою: "Чи буде розуміння списку спрацьовувати? Ні. Чи буде мені те, що мені потрібно, від того, щоб бути названою функцією? Ні. Добре добре: відсортовано (xs, key = lambda x: x.name, x.height) ", ймовірно, ви в кінці разів використовуєте лямбда потрібну кількість разів.
Аарон Маенпаа

4
+1: Я не можу наголосити на тому, що достатньо, коли людина використовує лямбда, використовує безіменну функцію. А імена мають цінну інтелектуальну додаткову цінність.
Стефан Ролланд

19

Я використовую Python вже кілька років, і ніколи не стикався з випадком, коли мені потрібна лямбда. Дійсно, як говорить підручник , це просто для синтаксичного цукру.


8
Вони дуже корисні при розробці графічного інтерфейсу за допомогою python. Часто віджети потребують посилання на функцію. Якщо вам потрібен віджет для виклику функції та передачі аргументів, лямбда - це дуже зручний спосіб зробити це.
Брайан Оуклі

1
Яка перевага перед передачею декількох аргументів у функцію? func_name (a, b): повертайте a + b, а не використовуючи лямбда
dter

2
це не потрібно, але це допомагає писати коротший код і в більшій кількості читати, особливо на багатослівних мовах, як Java або C ++
phuclv

17

Я не можу говорити про конкретну реалізацію лямбда python, але загалом функції лямбда справді зручні. Вони є основною технікою (можливо навіть ТЕХНІКА) функціонального програмування, і вони також дуже корисні в об'єктно-орієнтованих програмах. Для певних типів проблем вони найкраще рішення, тому, звичайно, не слід забувати!

Я пропоную вам прочитати про закриття та функцію карти (що посилається на документи python, але вона існує майже в кожній мові, яка підтримує функціональні конструкції), щоб побачити, чому це корисно.


3
Ці речі можна зробити без лямбда. Це просто великі клопоти.
Брайан

17

Функція лямбда - це небюрократичний спосіб створення функції.

Це воно. Наприклад, припустимо, що у вас є основна функція і вам потрібно призначити квадратні значення. Давайте розглянемо традиційний спосіб і лямбда-спосіб:

Традиційний спосіб:

def main():
...
...
y = square(some_number)
...
return something

def square(x):
    return x**2

Лямбда спосіб:

def main():
...
square = lambda x: x**2
y = square(some_number)
return something

Бачите різницю?

Функції лямбда дуже добре поєднуються зі списками, як, наприклад, розуміння списків або карта. Насправді, перелічення розуміння - це "пітонічний" спосіб виразити себе за допомогою лямбда. Наприклад:

>>>a = [1,2,3,4]
>>>[x**2 for x in a]
[1,4,9,16]

Давайте подивимося, що означають кожен елемент синтаксису:

[]: "Дайте мені список"

x ** 2: "використання цієї функції новонародженого"

для x in a: "у кожен елемент у"

Це зручно е-е? Створення таких функцій. Давайте перепишемо його за допомогою лямбда:

>>> square = lambda x: x**2
>>> [square(s) for x in a]
[1,4,9,16]

Тепер давайте скористаємося картою, яка є тим же самим, але більш нейтральною щодо мови. Карти приймають 2 аргументи:

(i) одна функція

(ii) ітерабельний

І дає вам список, де кожен елемент - це функція, застосована до кожного елементу, який можна повторити.

Отже, використовуючи карту, ми мали б:

>>> a = [1,2,3,4]
>>> squared_list = map(lambda x: x**2, a)

Якщо ви оволодієте лямбдами та картографуванням, у вас буде велика сила для маніпулювання даними та стислим способом. Функції лямбда ні є незрозумілими, ні забирають чіткість коду. Не плутайте щось важке з чимось новим. Як тільки ви почнете користуватися ними, вам це буде дуже зрозуміло.


13

Одна з приємних речей про lambda що, на мою думку, занижена, це те, що це спосіб відкласти оцінку для простих форм, поки значення не буде необхідним. Дозволь пояснити.

Багато процедур бібліотеки реалізовані так, що вони дозволяють називати певні параметри (з них одна лямбда). Ідея полягає в тому, що фактичне значення буде обчислюватися лише в той момент, коли воно буде використовуватися (а не тоді, коли воно викликається). Приклад (надуманий) може допомогти проілюструвати точку. Припустимо, у вас є розпорядок роботи, який збирався зробити журнал заданої часової позначки. Ви хочете, щоб рутина використовувала поточний час мінус 30 хвилин. Ви б назвали це так

log_timestamp(datetime.datetime.now() - datetime.timedelta(minutes = 30))

Тепер припустимо, що фактичну функцію буде викликано лише тоді, коли відбудеться певна подія, і ви хочете, щоб часова марка була обчислена лише в той час. Ви можете зробити це так

log_timestamp(lambda : datetime.datetime.now() - datetime.timedelta(minutes = 30))

Припускаючи, що log_timestampможе обробляти такі дзвінки, він оцінить це тоді, коли це потрібно, і ви отримаєте часову позначку на той час.

Звичайно, є альтернативні способи зробити це (наприклад, використовуючи operatorмодуль), але я сподіваюся, що я переказав це.

Оновлення : Ось дещо конкретніший приклад із реального світу.

Оновлення 2 : Я думаю, що це приклад того, що називається громом .


11

Як було сказано вище, лямбда-оператор в Python визначає анонімну функцію, а в Python функції - закриття. Важливо не плутати поняття закриття з лямбдаю оператора, що є для них просто синтаксичним метадоном.

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

В основному це зводиться до кількох моментів:

  • легше читати програмне забезпечення, яке прямо написано за допомогою значущих імен. Анонімні закриття за визначенням не можуть мати значущого імені, оскільки вони не мають імені. Ця стислість, здається, чомусь також заражає параметри лямбда, тому ми часто бачимо приклади, такі як лямбда x: x + 1
  • легше повторно використовувати іменовані закриття, оскільки вони можуть називатися по імені не один раз, коли є ім'я, на яке вони посилаються.
  • простіше відлагоджувати код, який використовує іменовані закриття замість лямбда, тому що ім'я з'явиться у відстеженнях та навколо помилки.

Це достатньо підстав, щоб округлити їх і перетворити на названі закриття. Однак я маю ще дві скарги на анонімні закриття.

Перша кривда - це просто ще одне непотрібне ключове слово, що захаращує мову.

Друга кривда є глибшою та на рівні парадигми, тобто мені не подобається, що вони просувають стиль функціонального програмування, тому що цей стиль менш гнучкий, ніж стилі передачі повідомлення, орієнтовані на об'єкти чи процедурні стилі, тому що обчислення лямбда не є Тьюрінг- повний (на щастя, в Python, ми все ще можемо вийти з цього обмеження навіть усередині лямбда). Я вважаю, що лямбди пропагують цей стиль:

  • Існує неявне повернення, тобто вони здаються функціями, які "повинні".

  • Вони є альтернативним механізмом, що приховує державу, до іншого, більш чіткого, читабельнішого, багаторазового використання та більш загального механізму: методів.

Я дуже стараюсь писати пігтон без лямбда та видаляти лямбда на виду. Я думаю, що Python був би дещо кращою мовою без лямбдів, але це лише моя думка.


1
"... в Python функції є закриттями". Це не зовсім правильно, як я це розумію. Закриття - це функції, але функції не завжди є закриттями. Функція-> лямбда х, у: х + у. Закриття-> лямбда х: лямбда у: х + у
0мат.

6
"через те, що числення лямбда не є повним Тюрінгом", явно неправильне, нетипізоване обчислення лямбда IS Turing завершене, це є причиною, чому вона є настільки важливою. Ви можете отримати рекурсію за допомогою Y-комбінатора,Y = lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args)))
Антті Хаапала,

Крім того, якщо хтось піде у Вікіпедію, щоб прочитати про повноту Тьюрінга, він говорить "Класичний приклад - обчислення лямбда".
Антті Хаапала

серйозно - Не Тюрінг завершений - ця відповідь потребує серйозного редагування чи відкликання.
Тоні Суффолк 66,

@ MarcinŁoś Оголошено, оскільки він дотримується KISS. Для порівняння, книга K&R зазначає, що, хоча використання завдання як частини або більшого виразу є більш компактним, воно часто є менш читабельним. Тенденцією використання функціональних моделей програмування є банальність і кліше. Достатньо вказати, яким чином вони можуть бути використані, але надмірно заявляти, що вони є першорядними для того, щоб стати компетентним розробником; повністю суб'єктивний. Цей аргумент є аналогом розробників C ++, які стверджують, що мови без класів примітивні, неповноцінні та неадекватні. «Тюрінг-повнота», аргументи є педантичними.
typedeaf

11

Лямбда - це насправді дуже потужні конструкції, що випливають із ідей функціонального програмування, і це - те, що аж ніяк не буде легко переглянуто, переосмислено або видалено найближчим часом Python. Вони допомагають вам написати код, який є більш потужним, оскільки він дозволяє передавати функції як параметри, таким чином, уявлення про функції як першокласні громадяни.

Лямбди, як правило, стають заплутаними, але після того, як буде отримано чітке розуміння, ви можете написати чистий елегантний код на зразок цього:

squared = map(lambda x: x*x, [1, 2, 3, 4, 5])

Вищевказаний рядок коду повертає список квадратів чисел у списку. Звичайно, ви також можете це зробити так:

def square(x):
    return x*x

squared = map(square, [1, 2, 3, 4, 5])

Очевидно, що колишній код коротший, і це особливо вірно, якщо ви маєте намір використовувати функцію map (або будь-яку подібну функцію, яка приймає функцію як параметр) лише в одному місці. Це також робить код більш інтуїтивним та елегантним.

Крім того, як згадував @David Заславський у своїй відповіді, розуміння списку - це не завжди шлях, особливо якщо ваш список повинен отримати значення якимось незрозумілим математичним шляхом.

З більш практичної точки зору, одна з найбільших переваг лямбдаз для мене останнім часом була в графічному інтерфейсі та керованому подіями програмуванні. Якщо ви подивитеся на зворотні дзвінки в Tkinter, все вони сприймають як аргументи - це подія, яка їх викликала. Напр

def define_bindings(widget):
    widget.bind("<Button-1>", do-something-cool)

def do-something-cool(event):
    #Your code to execute on the event trigger

А що робити, якщо у вас були аргументи? Щось таке просто, як передача 2 аргументів для збереження координат клацання мишею. Ви можете легко зробити так:

def main():
    # define widgets and other imp stuff
    x, y = None, None
    widget.bind("<Button-1>", lambda event: do-something-cool(x, y))

def do-something-cool(event, x, y):
    x = event.x
    y = event.y
    #Do other cool stuff

Тепер ви можете стверджувати, що це можна зробити за допомогою глобальних змінних, але чи справді ви хочете замахнутися на голову, турбуючись про управління пам’яттю та витоку, особливо якщо глобальна змінна буде просто використана в одному конкретному місці? Це був би просто поганий стиль програмування.

Коротше кажучи, лямбди є приголомшливими і їх ніколи не можна недооцінювати. Лямбди Python - це не те саме, що лямбди LISP (які є більш потужними), але ви дійсно можете зробити з ними багато магічних речей.


Дякую. Я зовсім не зрозумів твій останній приклад. Яким чином x і y визначаються в обох mainі do_something_cool? Що відбувається з xі yу функції? Передавані значення, здається, негайно перезаписуються? Як функція знає про це event? Чи можете ви додати коментарі / пояснення? Спасибі
Санджай Манохар

@SanjayManohar Я передаю, xі yв якості аргументів до do-something-coolта їх значень задаються функції, щоб проілюструвати, як ви можете використовувати лямбдаси для передачі аргументів там, де їх ніхто не очікує. widget.bindФункція очікує eventпараметр , який ідентифікує подію GUI на цьому конкретний віджеті. Для більшої чіткості рекомендую прочитати модель програмування Tkinter.
varagrawal

Хм, я думаю, я розумію модель Тінкіра. Але все ще не зовсім розумію - ви проходите x,yпотім робити x=event.x. Хіба це не переписує значення, яке ви передали? І як функція знає, що eventтаке? Я не бачу, куди ти передаєш цю функцію. Або це метод? Також вам дозволено мінус знаки в імені функції?
Санджай Манохар

@SanjayManohar приятель вам потрібно прочитати на Tkinter та Python. Tkinter передає об'єкту події функцію як дію за замовчуванням. Щодо x, y, це лише приклад для ілюстративних цілей. Я намагаюся показати силу лямбдів, а не Ткінтера. :)
varagrawal

1
Гаразд тепер я бачу, що ви планували - ви хочете, щоб функція отримувала event? у такому випадку чи не повинен ваш лямбда читати lambda event: do_something_cool(event,x,y)?
Санджай Манохар

8

Лямбди глибоко пов'язані з функціональним стилем програмування загалом. Google використовує ідею, що ви можете вирішити проблеми, застосувавши функцію до деяких даних та об'єднавши результати, для реалізації більшості своїх алгоритмів.

Програми, написані у стилі функціонального програмування, легко паралелізуються і, отже, стають все більш важливими для сучасних багатоядерних машин. Отже, коротше, НІ, ви не повинні їх забувати.


7

Перші привітання, яким вдалося розібратися лямбда. На мою думку, це дійсно потужна конструкція, з якою потрібно діяти. Тенденція цих днів до функціональних мов програмування, безумовно, є показником того, що цього не слід уникати, а також не буде переосмислено найближчим часом.

Ви просто повинні думати трохи інакше. Я впевнений, що скоро вам сподобається. Але будьте обережні, якщо ви маєте справу лише з пітоном. Оскільки лямбда - це не справжня зачинка, вона якось "ламається": ламбда пітонів розбита


Лямбда Пітона не порушена. Є два способи поводження з місцевими жителями в лямбдах. Обидва мають переваги та недоліки. Я думаю, що підхід Python (і C #), мабуть, є протиінтуїтивним для тих, хто звик до чисто функціональних мов, оскільки з чисто функціональною мовою я не думаю, що такий підхід навіть має сенс.
Брайан

Це дійсно контрінтуїтивно. Я не програміст пітонів, але в писклявій дрібниці це те саме, і я натрапляю на це регулярно. Тож навіть я вважав би це "зламаним" :)
Норберт Хартл

Ні, це не має нічого спільного з контрінтуїтивністю. Просто лексична сфера назв змінних - це функція; цикл не вводить лексичну область. Функції також працюють у Javascript. Якщо вам потрібна область для var, ви завжди можете це зробити (lambda scopedvar: lambda x: scopedvar + x) ()
Antti Haapala

6

Я тільки починаю Python і побіг головою спочатку в Lambda - що знадобило мені час, щоб зрозуміти.

Зауважте, що це не засудження нічого. У кожного різний набір речей, які не легко йдуть.

Чи є лямбда одним із тих "цікавих" мовних предметів, про які в реальному житті слід забути?

Ні.

Я впевнений, що є деякі крайні випадки, коли це може знадобитися, але зважаючи на неясність цього,

Це не незрозуміло. Останні 2 команди, над якими я працював, усі використовували цю функцію весь час.

потенціал його переосмислення в майбутніх випусках (моє припущення, засноване на різних його визначеннях)

Я не бачив жодних серйозних пропозицій переосмислити це в Python, окрім фіксації семантики закриття кілька років тому.

та знижена чіткість кодування - цього слід уникати?

Це не менш зрозуміло, якщо ви правильно це використовуєте. Навпаки, наявність більшої кількості мовних конструкцій збільшує чіткість.

Це нагадує мені про переповнення (переповнення буфера) типів C - вказівка ​​на верхню змінну та перевантаження для встановлення інших значень поля ... свого роду демонстрація техніки, але кошмар кодеру технічного обслуговування ..

Лямбда - це як переповнення буфера? Ого. Я не уявляю, як ти використовуєш лямбда, якщо ти вважаєш, що це "кошмар з обслуговування".


5
-1 за те, щоб я (та інші) знов прочитали ціле запитання. Зауважте, що іншим вдалося відповісти, не зробивши цього.
Paweł Polewicz

6

Я використовую лямбда, щоб уникнути дублювання коду. Це зробило б функцію легко зрозумілою, наприклад:

def a_func()
  ...
  if some_conditon:
     ...
     call_some_big_func(arg1, arg2, arg3, arg4...)
  else
     ...
     call_some_big_func(arg1, arg2, arg3, arg4...)

Я замінюю це тимчасовою лямбда

def a_func()
  ...
  call_big_f = lambda args_that_change: call_some_big_func(arg1, arg2, arg3, args_that_change)
  if some_conditon:
     ...
     call_big_f(argX)
  else
     ...
     call_big_f(argY)

5

Я почав сьогодні читати книгу Девіда Мерца «Обробка тексту на Python». Незважаючи на те, що він має досить короткий опис прикладів Ламбди в першій главі у поєднанні з поясненням у Додатку А змусив їх (нарешті) перейти зі сторінки, і раптом я зрозумів їх значення. Це не означає, що його пояснення буде працювати для вас, і я все ще перебуваю на стадії відкриття, тому я не буду намагатися додавати до цих відповідей, крім наступних: Я новачок у Python Я новачок в OOP Lambdas був боротьбою для мене Тепер, коли я читаю Мерц, я думаю, що я їх отримую, і вважаю їх дуже корисними, оскільки я думаю, що вони дозволяють більш чітко підходити до програмування.

Він відтворює дзен Python, один рядок якого є простим, ніж складним. Як програміст, який не читає OOP, читає код з лямбдами (і до списку розумінь до минулого тижня), я подумав: Це просто? . Я, нарешті, сьогодні зрозумів, що насправді ці функції роблять код набагато більш читабельним та зрозумілим, ніж альтернативний варіант, який незмінно є певним циклом. Я також зрозумів, що подібно до фінансової звітності, Python не розрахований на початківця користувача, скоріше він розроблений для користувача, який хоче отримати освіту. Я не можу повірити, наскільки потужна ця мова. Коли на мене настала (нарешті) мета та цінність лямбда, я хотів зірвати близько 30 програм і почати вводити лямбди, де це доречно.


5

Корисним випадком використання лямбда є поліпшення читабельності розуміння довгих списків . У цьому прикладі loop_dicкоротко для ясності, але уявіть, loop_dicщо це дуже довго. Якби ви просто використовували звичайне значення, яке містить iзамість лямбда-версії цього значення, ви отримаєте а NameError.

>>> lis = [{"name": "Peter"}, {"name": "Josef"}]

>>> loop_dic = lambda i: {"name": i["name"] + " Wallace" }
>>> new_lis = [loop_dic(i) for i in lis]

>>> new_lis
[{'name': 'Peter Wallace'}, {'name': 'Josef Wallace'}]

Замість

>>> lis = [{"name": "Peter"}, {"name": "Josef"}]

>>> new_lis = [{"name": i["name"] + " Wallace"} for i in lis]

>>> new_lis
[{'name': 'Peter Wallace'}, {'name': 'Josef Wallace'}]

5

Я можу навести вам приклад, де мені справді потрібна лямбда серйозна. Я роблю графічну програму, де користування правою кнопкою миші натискає на файл і призначає йому один із трьох варіантів. Виявляється, що в Tkinter (програма інтерфейсу GUI, про яку я це пишу), коли хтось натискає кнопку, він не може бути призначений команді, яка бере аргументи. Тож якщо я вибрав один із варіантів і хотів, щоб результатом мого вибору став:

print 'hi there'

Тоді нічого страшного. Але що робити, якщо мені потрібен мій вибір, щоб мати конкретну деталь. Наприклад, якщо я вибираю вибір A, він викликає функцію, яка бере якийсь аргумент, який залежить від вибору A, B або C, TKinter не може це підтримати. Ламда була єдиним варіантом, щоб обійти це фактично ...


2
Ну, мабуть, ви могли це зробити, def foo...а потім перейшли fooзамість цього lambda. Це просто більше коду, і ви повинні придумати ім'я.
Джон Кумбс

4

Я використовую його досить часто, головним чином як нульовий об’єкт або для часткового прив’язання параметрів до функції.

Ось приклади:

для реалізації нульового об'єкта:

{
    DATA_PACKET: self.handle_data_packets
    NET_PACKET: self.handle_hardware_packets
}.get(packet_type, lambda x : None)(payload)

для прив'язки параметра:

скажімо, у мене є такий API

def dump_hex(file, var)
    # some code
    pass

class X(object):
    #...
    def packet_received(data):
        # some kind of preprocessing
        self.callback(data)
    #...

Потім, коли я не хочу швидко скинути отримані дані у файл, я роблю це:

dump_file = file('hex_dump.txt','w')
X.callback = lambda (x): dump_hex(dump_file, x)
...
dump_file.close()

4

я використовую lambda для створення зворотних викликів, що включають параметри. Чистіше писати лямбда в один рядок, ніж писати метод для виконання того ж функціоналу.

Наприклад:

import imported.module

def func():
    return lambda: imported.module.method("foo", "bar")

на відміну від:

import imported.module

def func():
    def cb():
        return imported.module.method("foo", "bar")
    return cb

4

Я початківець пітон, тому для отримання чіткого уявлення про лямбда я порівняв його з циклом "для"; з точки зору ефективності. Ось код (python 2.7) -

import time
start = time.time() # Measure the time taken for execution

def first():
    squares = map(lambda x: x**2, range(10))
    # ^ Lambda
    end = time.time()
    elapsed = end - start
    print elapsed + ' seconds'
    return elapsed # gives 0.0 seconds

def second():
    lst = []
    for i in range(10):
        lst.append(i**2)
    # ^ a 'for' loop
    end = time.time()
    elapsed = end - start
    print elapsed + ' seconds'
    return elapsed # gives 0.0019998550415 seconds.

print abs(second() - first()) # Gives 0.0019998550415 seconds!(duh)

6
Можливо, вам буде цікавий timeitмодуль, який, як правило, дає більш точні результати, ніж time.time()значення віднімання .
Кевін

2
Хм, чи не потрібно перезапускати таймер на початку першого () та другого ()?
qneill

2

Лямбда - конструктор процедур. Ви можете синтезувати програми під час виконання, хоча лямбда Python не дуже потужна. Зауважте, що мало хто розуміє подібне програмування.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.