Каковы преимущества использования лямбд? [закрыто]
Проблема: Я пытаюсь разобраться с лямбда-функциями в Python. Является ли lambda
одной из тех "интересных" особенностей языка, которые в реальной жизни стоит забыть?
Я уверен, что существуют некоторые пограничные случаи, когда они могут понадобиться, но учитывая их неопределённость и возможность изменения в будущих версиях (это моё предположение, основанное на различных их определениях), а также ухудшение читаемости кода — стоит ли их избегать?
Это напоминает мне о переполнении буфера в C, когда указатель ссылается на верхнюю переменную и перезаписывает значения других полей. Это выглядит как некая техническая демонстрация, но является кошмаром для поддержки кода.
5 ответ(ов)
lambda
— это просто модное слово для обозначения функции
. За его названием не скрывается ничего запутанного, пугающего или загадочного. Когда вы читаете следующую строку, замените lambda
на функцию
в своем уме:
>>> f = lambda x: x + 1
>>> f(3)
4
Это просто определяет функцию от x
. В некоторых других языках, таких как R
, это выражается более явно:
> f = function(x) { x + 1 }
> f(3)
4
Видите? Это одна из самых естественных вещей в программировании.
В целом, всё, что можно сделать с помощью lambda
, можно сделать лучше с использованием именованных функций или выражений списков и генераторов.
Поэтому в большинстве случаев рекомендуется использовать именно один из этих подходов в любых ситуациях (за исключением, возможно, случаев с временным кодом, написанным в интерактивном интерпретаторе).
Лямбда-функция — это небюрократичный способ создания функции.
Например, представим, что у вас есть основная функция, и вам нужно возвести в квадрат значения. Давайте посмотрим, как это можно сделать традиционным способом и с помощью лямбда-функции:
Традиционный способ:
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
Видите разницу?
Лямбда-функции отлично работают со списками, например, в списковых выражениях или функции map
. На самом деле, списковые выражения — это "питонический" способ выразить себя, используя лямбды. Пример:
>>> a = [1, 2, 3, 4]
>>> [x**2 for x in a]
[1, 4, 9, 16]
Давайте разберем, что означает каждый элемент синтаксиса:
[]
: "Дайте мне список"x**2
: "используя эту вновь созданную функцию"for x in a
: "для каждого элемента вa
"
Удобно, да? Создавать функции таким образом. Давайте перепишем это с использованием лямбды:
>>> square = lambda x: x**2
>>> [square(s) for s in a]
[1, 4, 9, 16]
Теперь давайте используем map
, который делает то же самое, но более универсально. map
принимает два аргумента:
- одна функция
- перебираемый объект (итератор)
И возвращает список, где каждый элемент — это результат применения функции к каждому элементу перебираемого объекта.
Таким образом, с использованием map
мы можем сделать следующее:
>>> a = [1, 2, 3, 4]
>>> squared_list = list(map(lambda x: x**2, a))
Если вы овладеете лямбдами и map
, вы получите большую силу для манипуляции данными и сможете делать это лаконично. Лямбда-функции не являются ни запутанными, ни ухудшают читаемость кода. Не путайте что-то сложное с чем-то новым. Как только вы начнете их использовать, вы поймете, что это очень прозрачно.
Как уже упоминалось, оператор lambda в Python определяет анонимную функцию, а функции в Python являются замыканиями. Важно не путать концепцию замыканий с оператором lambda, который является лишь синтаксическим лекарством для них.
Когда я начинал изучать Python несколько лет назад, я активно использовал лямбды, думая, что это круто, наряду с генераторами списков. Однако я пишу и поддерживаю большой веб-сайт на Python, в котором порядка нескольких тысяч функциональных точек. На своем опыте я научился, что лямбды могут быть полезными для прототипирования, но не предлагают никаких преимуществ по сравнению с именованными замыканиями, кроме как сэкономить несколько нажатий клавиш, и то не всегда.
В целом, это сводится к нескольким пунктам:
- Проще читать код, который явно написан с использованием значимых имен. Анонимные замыкания по определению не могут иметь значимого имени, так как у них нет имени. Эта краткость, почему-то, также "заражает" параметры лямбды, поэтому мы часто видим примеры вроде
lambda x: x + 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):
# Ваш код для выполнения при срабатывании события
А что, если вам нужно передать какие-то аргументы? Например, простую задачу — передать 2 аргумента для хранения координат клика мышью. Вы легко можете сделать это так:
def main():
# опишите виджеты и другие важные вещи
x, y = None, None
widget.bind("<Button-1>", lambda event: do_something_cool(event, x, y))
def do_something_cool(event, x, y):
x = event.x
y = event.y
# Выполните другие полезные действия
Можно возразить, что это можно сделать, используя глобальные переменные, но не хотите ли вы волноваться о управлении памятью и утечками, особенно если глобальная переменная будет использоваться только в одном конкретном месте? Это было бы просто плохим стилем программирования.
В общем, лямбда-функции замечательны и их никогда нельзя недооценивать. Лямбды в Python не являются тем же самым, что и лямбды в LISP (которые более мощные), но с ними можно сделать много интересных и полезных вещей.
Как создать декораторы функций и объединить их?
Сравнение: генераторы списков против lambda + filter
Как получить имя функции в виде строки?
Как использовать десятичное значение шага в range()?
Как отсортировать список/кортеж списков/кортежей по элементу на заданном индексе