Лямбда-функции в Python: lambda на примерах
Лямбда функция в Python - это короткая безымянная функция из одного выражения, которую создают ключевым словом lambda прямо там, где она нужна. Ниже разобраны синтаксис, отличия от def, типичные сценарии с sorted, map и filter, условия внутри лямбды и ошибки, на которых чаще всего спотыкаются.

Содержание статьи
Синтаксис lambda
Лямбда записывается так: ключевое слово lambda, затем аргументы через запятую, двоеточие и одно выражение. Результат этого выражения функция возвращает автоматически, слово return не пишется.
lambda аргументы: выражениеАргументов может быть сколько угодно, в том числе ноль. Работают значения по умолчанию, *args и **kwargs, как у обычных функций.
double = lambda x: x * 2
add = lambda a, b=10: a + b
total = lambda *nums: sum(nums)
hello = lambda: "Привет"
print(double(5)) # 10
print(add(1)) # 11
print(total(1, 2, 3)) # 6
print(hello()) # Привет
print((lambda x, y: x * y)(3, 4)) # 12Последняя строка показывает, что лямбду можно вызвать сразу в месте создания. На практике так делают редко, но пример хорошо показывает суть: lambda - это выражение, которое возвращает объект функции.
Отличия от обычной функции
Функция double из примера выше полностью эквивалентна такой записи через def:
def double(x):
return x * 2Обе создают объект одного типа function, разница только в возможностях записи.
| Возможность | lambda | def |
|---|---|---|
| Имя функции | Нет, в трассировке видно <lambda> | Есть |
| Тело | Одно выражение | Любое число инструкций |
| return, циклы, присваивание через = | Нельзя | Можно |
| Строка документации и аннотации типов | Нет | Есть |
| Где пишется | Внутри выражения, например в аргументе | Отдельной инструкцией |
Из таблицы вытекает правило: lambda нужна для коротких одноразовых функций, которые передаются как аргумент. Присваивать лямбду переменной, как double = lambda x: x * 2, руководство по стилю PEP 8 не рекомендует. Если функции нужно имя, пишите def: в сообщениях об ошибках будет видно понятное название.
Ключ для sorted, min и max
Самое частое применение лямбды - параметр key. Функции sorted(), min(), max() и метод list.sort() вызывают key для каждого элемента и сравнивают полученные значения, а не сами элементы.
words = ["кот", "Арбуз", "ёж", "банан"]
print(sorted(words, key=lambda w: len(w)))
# ['ёж', 'кот', 'Арбуз', 'банан']
users = [("Анна", 31), ("Борис", 25), ("Вера", 28)]
print(sorted(users, key=lambda u: u[1]))
# [('Борис', 25), ('Вера', 28), ('Анна', 31)]
print(max(users, key=lambda u: u[1])) # ('Анна', 31)
prices = {"чай": 120, "кофе": 350, "сок": 90}
print(sorted(prices.items(), key=lambda kv: kv[1], reverse=True))
# [('кофе', 350), ('чай', 120), ('сок', 90)]Для сортировки по нескольким признакам лямбда возвращает кортеж. Например, key=lambda u: (-u[1], u[0]) упорядочит по убыванию возраста, а при равном возрасте по имени. Больше примеров в статье про сортировку списков.
Лямбда в map, filter и reduce
Функции высшего порядка принимают другую функцию аргументом. map() применяет ее к каждому элементу, filter() оставляет элементы, для которых она вернула истину, functools.reduce() сворачивает последовательность в одно значение. Все три возвращают результат лениво, поэтому map и filter оборачивают в list().
from functools import reduce
nums = [1, 5, 4, 6, 8, 11, 3, 12]
print(list(filter(lambda x: x % 2 == 0, nums))) # [4, 6, 8, 12]
print(list(map(lambda x: x * 2, nums))) # [2, 10, 8, 12, 16, 22, 6, 24]
print(reduce(lambda acc, x: acc * x, [1, 2, 3, 4])) # 24Те же задачи часто короче и понятнее решаются генератором списка: [x * 2 for x in nums] вместо map и [x for x in nums if x % 2 == 0] вместо filter. Связка map с lambda выгодна, когда функция уже готова и лямбда не нужна вовсе: list(map(str.upper, words)). Подробнее о фильтрации в статье про функцию filter.
Условие внутри лямбды
Инструкция if в лямбде недоступна, но тернарный оператор является выражением, поэтому его использовать можно. Ветка else обязательна.
parity = lambda n: "четное" if n % 2 == 0 else "нечетное"
print(parity(7)) # нечетное
grade = lambda s: "отлично" if s >= 90 else "хорошо" if s >= 70 else "слабо"
print(grade(75)) # хорошоВложенные тернарные операторы читаются плохо. Если веток больше двух, обычная функция с if и elif будет понятнее.
Подводные камни
Главная ловушка связана с переменными из внешней области. Лямбда запоминает не значение переменной, а саму переменную и читает ее в момент вызова. Если создать лямбды в цикле, все они увидят последнее значение счетчика.
funcs = [lambda: i for i in range(3)]
print([f() for f in funcs]) # [2, 2, 2]
funcs = [lambda i=i: i for i in range(3)]
print([f() for f in funcs]) # [0, 1, 2]Исправление во второй строке работает потому, что значение по умолчанию вычисляется в момент создания функции. Механизм подробно разобран в статье про замыкания.
Другие частые ошибки:
- Попытка написать присваивание:
lambda x: y = xвызывает SyntaxError. Оператор := формально допустим, но в лямбде делает код запутанным. - Лишняя обертка:
key=lambda x: len(x)можно записать какkey=len. - Сложная логика в одну строку. Если лямбда не помещается в строку или требует комментария, превратите ее в def.
Частые вопросы
Ответы на вопросы, которые чаще всего задают о lambda.
Почему лямбда называется анонимной?
Выражение lambda создает функцию без имени. Даже если присвоить ее переменной, атрибут __name__ останется равен "<lambda>", а не имени переменной.
Может ли лямбда содержать несколько строк?
Нет, тело ограничено одним выражением. Его можно перенести на несколько строк внутри скобок, но инструкции вроде for, while или try внутри лямбды недопустимы.
Быстрее ли lambda обычной функции?
Нет, скорость одинаковая: обе компилируются в один и тот же тип объекта. Выбор между ними делают по удобству чтения, а не по производительности.
Можно ли вернуть несколько значений из лямбды?
Да, если вернуть кортеж: lambda x: (x, x ** 2). Скобки обязательны, иначе запятая будет воспринята как разделитель аргументов внешней функции.