Чем полезны лямбды Python?
Я пытаюсь выяснить Python лямбды. Является ли лямбда одним из тех "интересных" языковых предметов, которые в реальной жизни следует забыть?
Я уверен, что есть некоторые крайние случаи, когда это может понадобиться, но, учитывая его неясность, возможность его переопределения в будущих выпусках (мое предположение, основанное на его различных определениях) и снижение ясности кодирования - если это произойдет избегать?
Это напоминает мне о переполнении (переполнении буфера) типов Си - указание на верхнюю переменную и перегрузка для установки других значений поля. Это похоже на техническое мастерство, но кошмар технического обслуживания.
26 ответов
Вы говорите о лямбда-функциях? подобно
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]
Я использую лямбда-функции на регулярной основе. Мне потребовалось некоторое время, чтобы привыкнуть к ним, но в конце концов я понял, что они очень ценная часть языка.
lambda
это просто модный способ сказать function
, Кроме его имени, в этом нет ничего неясного, пугающего или загадочного. Когда вы прочитаете следующую строку, замените lambda
от function
в твоих мыслях:
>>> f = lambda x: x + 1
>>> f(3)
4
Это просто определяет функцию x
, Некоторые другие языки, такие как R
, скажи это явно:
> f = function(x) { x + 1 }
> f(3)
4
Ты видишь? Это одна из самых естественных вещей в программировании.
Двухстрочная сводка:
- Закрытие: очень полезно. Изучи их, используй их, люби их.
- Питона
lambda
Ключевое слово: ненужно, иногда полезно. Если вы обнаружите, что делаете с ним что-то сложное, уберите это и определите реальную функцию.
Лямбда является частью очень важного механизма абстракции, который имеет дело с функциями более высокого порядка. Чтобы правильно понять его ценность, посмотрите высококачественные уроки Абельсона и Суссмана и прочитайте книгу SICP.
Это актуальные проблемы современного программного обеспечения, которые становятся все более популярными.
Я сомневаюсь, что лямбда уйдет. Посмотрите пост Гвидо о том, что, наконец, он перестал пытаться удалить его. Также см. Схему конфликта.
Вы можете проверить этот пост для получения дополнительной информации о сделке, стоящей за функциональными возможностями Python: http://python-history.blogspot.com/2009/04/origins-of-pythons-functional-features.html
Любопытно, что функции сопоставления, фильтрации и сокращения, которые изначально мотивировали введение лямбда-функции и других функциональных функций, были в значительной степени заменены списками и генераторами выражений. Фактически, функция Reduce была удалена из списка встроенных функций в Python 3.0. (Тем не менее, нет необходимости отправлять жалобы на удаление лямбды, карты или фильтра: они остаются.:-)
Мои собственные два цента: Редко лямбда стоит того, насколько ясность идет. Обычно есть более четкое решение, которое не включает лямбду.
Лямбды чрезвычайно полезны в программировании GUI. Например, допустим, вы создаете группу кнопок и хотите использовать один параметризованный обратный вызов, а не уникальный обратный вызов для каждой кнопки. Лямбда позволяет вам сделать это с легкостью:
for value in ["one","two","three"]:
b = tk.Button(label=value, command=lambda arg=value: my_callback(arg))
b.pack()
(Примечание: хотя этот вопрос конкретно задает lambda
Вы также можете использовать functools.partial, чтобы получить тот же тип результата)
Альтернативой является создание отдельного обратного вызова для каждой кнопки, что может привести к дублированию кода.
В 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 - у меня никогда не было необходимости использовать их (или я видел такое, где бы лучше использовать обычную функцию, понимание списка или одну из многих встроенных функций)
Для совершенно случайного примера из статьи "Лямбда 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
аргумент и вызвать ошибку. Использование лямбды означает, что мы можем аккуратно отбросить аргумент без необходимости определения функции обтекания.
Я нахожу лямбду полезной для списка функций, которые делают то же самое, но для разных обстоятельств. Как и во множественном числе в 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()
это когда вы зависите от переменной функции id.
Практически все, что вы можете сделать с lambda
Вы можете добиться большего с помощью либо именованных функций, либо выражений списка и генератора.
Следовательно, по большей части вы должны использовать только один из них практически в любой ситуации (за исключением, может быть, рабочего кода, написанного в интерактивном интерпретаторе).
Я использую Python в течение нескольких лет, и я никогда не сталкивался с ситуацией, когда мне нужна лямбда. На самом деле, как говорится в учебнике, это только для синтаксического сахара.
Лямбда-функция - это небюрократический способ создания функции.
Вот и все. Например, давайте предположим, что у вас есть основная функция, и вам нужно возвести в квадрат значения. Давайте посмотрим традиционный и лямбда-способ сделать это:
Традиционный способ:
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]
Давайте посмотрим, что означает каждый элемент синтаксиса:
[]: "Дайте мне список"
х **2: "используя эту функцию новорожденного"
для х в: "в каждый элемент в"
Это удобно, а? Создание таких функций. Давайте перепишем это с помощью лямбды:
>>> square = lambda x: x**2
>>> [square(s) for x in a]
[1,4,9,16]
Теперь давайте воспользуемся картой, которая та же самая вещь, но более независимая от языка. Карты принимает 2 аргумента:
(я) одна функция
(ii) повторяемый
И дает вам список, где каждый элемент - это функция, применяемая к каждому элементу итерируемого.
Итак, используя карту мы получили бы:
>>> a = [1,2,3,4]
>>> squared_list = map(lambda x: x**2, a)
Если вы овладеете лямбдами и картографированием, у вас будет великая сила манипулировать данными и быть лаконичной. Лямбда-функции не являются ни темными, ни лишающими ясности кода. Не путайте что-то сложное с чем-то новым. Как только вы начнете использовать их, вы поймете это очень ясно.
Я не могу говорить о конкретной реализации лямбды в Python, но в целом лямбда-функции действительно удобны. Они являются основным методом (возможно, даже методом) функционального программирования, и они также очень полезны в объектно-ориентированных программах. Для определенных типов проблем, они являются лучшим решением, поэтому, конечно, не следует забывать!
Я предлагаю вам ознакомиться с замыканиями и функцией map (которая ссылается на документы Python, но она существует почти на каждом языке, поддерживающем функциональные конструкции), чтобы понять, почему это полезно.
Одна из приятных вещей о 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: я думаю, что это пример того, что называется гром.
Как указано выше, лямбда-оператор в Python определяет анонимную функцию, а в Python функции являются замыканиями. Важно не путать концепцию замыканий с оператором лямбда, который для них является просто синтаксическим метадоном.
Когда я начал работать с Python несколько лет назад, я часто использовал лямбды, думая, что они классные, наряду со списками. Тем не менее, я написал и должен поддерживать большой веб-сайт, написанный на Python, с порядка нескольких тысяч функциональных точек. Из опыта я узнал, что лямбды могут быть в порядке для создания прототипов, но не предлагают ничего, кроме встроенных функций (именованных замыканий), за исключением сохранения нескольких нажатий клавиш, а иногда и нет.
В основном это сводится к нескольким пунктам:
- легче читать программное обеспечение, которое явно написано с использованием значимых имен. Анонимные замыкания по определению не могут иметь значимого имени, поскольку у них нет имени. Эта краткость, по какой-то причине, кажется, также заражает лямбда-параметры, поэтому мы часто видим такие примеры, как лямбда х: х +1
- именованные замыкания проще использовать повторно, так как на них можно ссылаться по имени более одного раза, когда есть имя, к которому они могут обращаться.
- легче отлаживать код, использующий именованные замыкания вместо лямбда-выражений, потому что имя будет отображаться в трассировках и вокруг ошибки.
Этого достаточно, чтобы округлить их и преобразовать в именованные замыкания. Тем не менее, я держу два других недовольства анонимным закрытием.
Первое недовольство в том, что это просто лишнее ключевое слово, загромождающее язык.
Второе недовольство более глубокое и на уровне парадигмы, то есть мне не нравится, что они продвигают стиль функционального программирования, потому что этот стиль менее гибок, чем стиль передачи сообщений, объектно-ориентированный или процедурный, потому что лямбда-исчисление не является тьюрингово-исчислением. завершено (к счастью, в Python мы можем выйти из этого ограничения даже внутри лямбды). Я считаю, что лямбды продвигают этот стиль по следующим причинам:
Существует неявный возврат, то есть они кажутся "должны" быть функциями.
Они представляют собой альтернативный механизм скрытия состояний другому, более явному, более читаемому, более повторно используемому и более общему механизму: методам.
Я очень стараюсь писать Python без лямбды, и удаляю лямбды на месте. Я думаю, что Python был бы немного лучше без лямбд, но это только мое мнение.
Лямбды на самом деле являются очень мощными конструкциями, основанными на идеях функционального программирования, и это то, что никоим образом не будет легко пересматриваться, переопределяться или удаляться в ближайшем будущем 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 Zaslavsky, списочные понимания не всегда являются подходящим способом, особенно если ваш список должен получать значения каким-то неясным математическим способом.
С более практической точки зрения, одно из самых больших преимуществ лямбды для меня в последнее время было в GUI и программировании на основе событий. Если вы посмотрите на обратные вызовы в 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 (которые более мощные), но вы действительно можете делать с ними много волшебных вещей.
Лямбды тесно связаны с функциональным стилем программирования в целом. Идея, что вы можете решать проблемы, применяя функцию к некоторым данным и объединяя результаты, - вот что Google использует для реализации большинства своих алгоритмов.
Программы, написанные в стиле функционального программирования, легко распараллеливаются и, следовательно, становятся все более и более важными для современных многоядерных машин. Короче говоря, НЕТ, вы не должны забывать их.
Первые поздравления, которые удалось выяснить лямбда. На мой взгляд, это действительно мощная конструкция для работы. Сегодняшняя тенденция к функциональным языкам программирования, несомненно, является показателем того, что его не следует ни избегать, ни пересматривать в ближайшем будущем.
Вы просто должны думать немного по-другому. Я уверен, что скоро вам это понравится. Но будьте осторожны, если вы имеете дело только с питоном. Поскольку лямбда не является настоящим закрытием, она как-то "сломана": лямбда-питоны сломана
Я использую лямбды, чтобы избежать дублирования кода. Это сделало бы функцию легко понятной, например:
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)
Полезным примером использования лямбд является улучшение читаемости длинных списков. В этом примере 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'}]
Я только начинаю Python и побежал головой в Лямбду, что заняло у меня некоторое время, чтобы понять.
Обратите внимание, что это не осуждение чего-либо. У всех есть разные вещи, которые не всегда легко найти.
Является ли лямбда одним из тех "интересных" языковых предметов, которые в реальной жизни следует забыть?
Нет.
Я уверен, что есть некоторые крайние случаи, когда это могло бы быть необходимо, но учитывая неясность этого,
Это не неизвестно. Последние 2 команды, над которыми я работал, все использовали эту функцию все время.
возможность его переопределения в будущих выпусках (мое предположение основано на его различных определениях)
Я не видел серьезных предложений переопределить его в Python, кроме исправления семантики замыкания несколько лет назад.
и уменьшенная ясность кодирования - этого следует избегать?
Не менее понятно, правильно ли вы это используете. Напротив, наличие большего количества языковых конструкций повышает ясность.
Это напоминает мне о переполнении (переполнении буфера) типов C - указание на верхнюю переменную и перегрузка для установки других значений поля... своего рода техническое мастерство, но кошмар кодировщика обслуживания...
Лямбда это как переполнение буфера? Вот это да. Я не могу представить, как вы используете лямбду, если думаете, что это "кошмар обслуживания".
Я могу привести пример, где мне действительно нужна серьезная лямбда. Я делаю графическую программу, где использую правой кнопкой мыши файл и назначаю ему один из трех вариантов. Оказывается, что в Tkinter (программе интерфейса GUI, в которой я пишу это), когда кто-то нажимает кнопку, он не может быть назначен команде, которая принимает аргументы. Поэтому, если я выбрал один из вариантов и хотел, чтобы результатом моего выбора было:
print 'hi there'
Тогда ничего страшного. Но что, если мне нужен мой выбор, чтобы иметь конкретную деталь? Например, если я выбираю вариант A, он вызывает функцию, которая принимает некоторый аргумент, зависящий от выбора A, B или C, TKinter не может это поддерживать. Ламда была единственной возможностью обойти это на самом деле...
Я новичок в Python, поэтому, чтобы получить четкое представление о лямбде, я сравнил его с циклом for; с точки зрения эффективности. Вот код (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)
Я начал читать книгу Дэвида Мерца сегодня "Обработка текста в Python". В то время как у него есть довольно лаконичное описание лямбды, примеры в первой главе в сочетании с объяснением в Приложении А заставили их спрыгнуть со страницы (наконец) для меня, и внезапно я понял их ценность. Это не значит, что его объяснение сработает для вас, и я все еще на стадии открытия, поэтому я не буду пытаться добавить к этим ответам, кроме следующего: я новичок в Python, я новичок в ООП. Лямбды были борьбой за меня. Теперь, когда я прочитал Mertz, я думаю, что получил их, и считаю их очень полезными, так как считаю, что они позволяют более чистый подход к программированию.
Он воспроизводит Дзен Питона, одна строка которого Проста лучше, чем сложна. Как программист, не являющийся ООП, читающий код с лямбдами (и до прошлой недели в списках), я подумал:это просто?, Сегодня я наконец понял, что на самом деле эти функции делают код намного более читабельным и понятным, чем альтернатива, которая неизменно представляет собой своего рода цикл. Я также понял, что, как и финансовые отчеты, Python не предназначен для начинающего пользователя, скорее он предназначен для пользователя, который хочет получить образование. Я не могу поверить, насколько силен этот язык. Когда до меня дошло (наконец-то) цель и значение лямбд, я хотел разорвать около 30 программ и начать заново вводить лямбды, где это уместно.
Я использую его довольно часто, в основном как нулевой объект или для частичного связывания параметров с функцией.
Вот примеры:
реализовать шаблон нулевого объекта:
{
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()
Я использую 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
Лямбда - это конструктор процедур. Вы можете синтезировать программы во время выполнения, хотя лямбда Python не очень мощная. Обратите внимание, что мало кто понимает такой вид программирования.