Что такое lambda в Python и зачем она нужна, если есть обычные функции через def?
Постоянно вижу в чужом коде такие штуки: sorted(users, key=lambda u: u.age). Что это за lambda? Вроде какая-то функция, но без имени и без def. Не понимаю, зачем вообще нужна, если можно написать нормальную функцию. Когда её уместно использовать, а когда нет?
2 ответа
lambda — это способ создать маленькую анонимную функцию прямо в одну строку, без def и без имени. Синтаксис такой:
lambda аргументы: выражение
Результат выражения автоматически возвращается (никакого return писать не надо). Вот две полностью эквивалентные функции:
def square(x):
return x * x
square2 = lambda x: x * x
square(5) # 25
square2(5) # 25
Но хранить лямбду в переменной — плохой стиль (для этого есть def). Её сила — когда нужна функция на один раз, которую передают как аргумент. Самый частый случай — параметр key= в sorted, max, min:
users = [("Аня", 30), ("Боб", 25), ("Вера", 28)]
# сортировка по возрасту (второй элемент кортежа)
print(sorted(users, key=lambda u: u[1]))
# [('Боб', 25), ('Вера', 28), ('Аня', 30)]
# самый старший
print(max(users, key=lambda u: u[1])) # ('Аня', 30)
Здесь key говорит: «по какому признаку сравнивать элементы». Лямбда достаёт этот признак. Заводить ради этого отдельную именованную функцию — избыточно.
Также лямбды часто суют в map и filter:
nums = [1, 2, 3, 4]
list(map(lambda x: x * 2, nums)) # [2, 4, 6, 8]
list(filter(lambda x: x % 2 == 0, nums)) # [2, 4]
Хотя тут многие питонисты предпочтут списочные включения как более читаемые: [x * 2 for x in nums].
Когда лучше обычный def: если логика не помещается в одно выражение, нужны несколько строк, if/else-ветвления или просто хочется внятное имя для отладки. Лямбда хороша ровно для коротких одноразовых выражений — не пытайся впихнуть в неё много логики.
Подсвечу классическую ловушку с лямбдами в цикле — она реально кусает новичков:
funcs = [lambda: i for i in range(3)]
print([f() for f in funcs]) # [2, 2, 2], а НЕ [0, 1, 2]
Лямбда захватывает переменную i по ссылке, а не её значение в момент создания. К моменту вызова цикл закончился и i == 2. Лечится привязкой через аргумент по умолчанию:
funcs = [lambda i=i: i for i in range(3)]
print([f() for f in funcs]) # [0, 1, 2]
И ещё мелочь по стилю: PEP 8 не рекомендует присваивать лямбду переменной (f = lambda x: ...). Если нужно имя — пиши def.