lambda-функции в Python
Что такое lambda в Python: синтаксис анонимной функции, lambda в sorted, max, map и filter, условие внутри lambda, ловушка с циклом и когда лучше def.
Содержание · 10 разделов
- Синтаксис lambda
- lambda и def: в чём разница
- lambda как ключ сортировки: sorted, min, max
- lambda в map, filter и reduce
- Условие внутри lambda
- Другие места, где пригодится lambda
- Когда lambda не нужна
- Уже есть готовая функция
- lambda, сохранённая в переменную
- Логика длиннее одной строки
- Частые ошибки
- return или присваивание внутри lambda
- Переменная цикла в lambda: все функции возвращают одно значение
- Забыли вызвать или передали результат вызова
- Запятая после выражения
- Частые вопросы
- Можно ли вызвать print внутри lambda?
- lambda работает быстрее, чем def?
- Можно ли написать рекурсивную lambda?
- Бывает ли асинхронная lambda?
- Можно ли передать lambda в default поля модели Django?
- Итог
lambda в Python создаёт короткую анонимную функцию из одного выражения: lambda аргументы: выражение. Результат выражения возвращается автоматически, return не пишется. Короткий ответ:
square = lambda x: x * x
print(square(5)) # → 25
print((lambda a, b: a + b)(2, 3)) # → 5
На практике lambda почти никогда не присваивают переменной, а передают прямо в другую функцию — чаще всего как аргумент key в sorted(), min() и max() или в map() и filter(). Ниже — синтаксис, типичные сценарии, ограничения и ошибки. Как устроены обычные функции с def, читайте в статье о функциях в Python. Примеры проверены на Python 3.12.
Синтаксис lambda
lambda параметры: выражение
- Параметров может быть сколько угодно, в том числе ноль.
- После двоеточия — ровно одно выражение. Его значение и есть результат функции.
- Поддерживаются те же виды параметров, что и в
def: значения по умолчанию,*args,**kwargs.
no_args = lambda: "готово"
two = lambda a, b: a * b
default = lambda name, greeting="Привет": f"{greeting}, {name}!"
star = lambda *nums: sum(nums)
print(no_args()) # → готово
print(two(3, 4)) # → 12
print(default("Анна")) # → Привет, Анна!
print(default("Олег", greeting="Здравствуйте")) # → Здравствуйте, Олег!
print(star(1, 2, 3)) # → 6
lambda и def: в чём разница
lambda создаёт объект того же типа function, что и def. Различаются они синтаксисом и возможностями:
def square_def(x):
return x * x
square = lambda x: x * x
print(square_def(4) == square(4)) # → True
print(square_def.__name__, square.__name__) # → square_def <lambda>
print(type(square)) # → <class 'function'>
| Признак | def | lambda |
|---|---|---|
| Имя | есть (square_def) | <lambda> |
| Тело | любые инструкции, много строк | одно выражение |
return | явный | неявный — результат выражения |
| Docstring и аннотации типов | да | нет |
| Где уместна | везде | короткая функция «на месте» |
Из-за отсутствия имени в traceback ошибка внутри lambda видна как in <lambda> — по такой строке сложнее понять, какая именно функция упала.
lambda как ключ сортировки: sorted, min, max
Главный сценарий — параметр key. Функция-ключ получает элемент и возвращает значение, по которому элементы сравниваются:
words = ["pear", "fig", "Kiwi", "date", "apple"]
print(sorted(words, key=lambda s: len(s)))
# → ['fig', 'pear', 'Kiwi', 'date', 'apple']
print(sorted(words, key=lambda s: (len(s), s.lower())))
# → ['fig', 'date', 'Kiwi', 'pear', 'apple']
Во втором примере lambda возвращает кортеж: сначала слова сравниваются по длине, при равенстве — по алфавиту без учёта регистра.
Сортировка списка словарей и поиск максимума по полю:
users = [
{"name": "Олег", "age": 25},
{"name": "Анна", "age": 31},
{"name": "Иван", "age": 19},
]
print(sorted(users, key=lambda u: u["age"])[0]["name"]) # → Иван
print(max(users, key=lambda u: u["age"])["name"]) # → Анна
users.sort(key=lambda u: u["name"]) # сортировка на месте
print([u["name"] for u in users]) # → ['Анна', 'Иван', 'Олег']
Сортировка словаря по значению — тот же приём с items():
scores = {"Олег": 72, "Анна": 95, "Иван": 88}
print(sorted(scores.items(), key=lambda item: item[1], reverse=True))
# → [('Анна', 95), ('Иван', 88), ('Олег', 72)]
Больше приёмов работы со словарями — в статье о словарях Python, со списками — в статье о списках.
lambda в map, filter и reduce
from functools import reduce
nums = [1, 2, 3, 4, 5, 6]
print(list(map(lambda n: n * 10, nums))) # → [10, 20, 30, 40, 50, 60]
print(list(filter(lambda n: n % 2 == 0, nums))) # → [2, 4, 6]
print(reduce(lambda acc, n: acc + n, nums)) # → 21
map() и filter() возвращают ленивые итераторы, поэтому результат обёрнут в list(). Те же задачи часто короче решаются генератором списка: [n * 10 for n in nums] и [n for n in nums if n % 2 == 0]. Когда что выбирать, подробно разобрано в статье о функции map в Python.
Условие внутри lambda
Инструкцию if в lambda написать нельзя, но можно использовать условное выражение a if условие else b:
sign = lambda n: "положительное" if n > 0 else ("ноль" if n == 0 else "отрицательное")
print(sign(5), sign(0), sign(-3)) # → положительное ноль отрицательное
Если ветвлений больше двух, такую строку трудно читать — перепишите её обычной функцией с if/elif/else. Синтаксис условий и тернарного оператора описан в статье об условиях if else в Python.
Другие места, где пригодится lambda
- Фабрика значений по умолчанию:
defaultdict(lambda: "нет данных")— функцию без аргументов вызывают для каждого отсутствующего ключа. - Обработчики событий в GUI: в Tkinter
command=lambda: save(file_name)передаёт кнопке функцию с заранее подставленным аргументом. - Небольшие преобразования в pandas:
df["price"].apply(lambda p: p * 1.2).
Когда lambda не нужна
Уже есть готовая функция
Не оборачивайте в lambda то, что и так является функцией:
words = ["banana", "Apple", "cherry"]
sorted(words, key=lambda w: len(w)) # лишняя обёртка
sorted(words, key=len) # то же самое
sorted(words, key=str.lower) # метод как функция
Для доступа к ключу словаря или элементу кортежа есть operator.itemgetter, для атрибутов объекта — operator.attrgetter:
from operator import itemgetter
users = [{"name": "Олег", "age": 25}, {"name": "Анна", "age": 31}]
print([u["name"] for u in sorted(users, key=itemgetter("age"))]) # → ['Олег', 'Анна']
lambda, сохранённая в переменную
Запись square = lambda x: x * x работает, но руководство по стилю PEP 8 рекомендует в таком случае писать def: функция получит нормальное имя в traceback, её можно снабдить docstring и аннотациями. Линтеры (flake8, Ruff) помечают такое присваивание правилом E731.
Логика длиннее одной строки
Если хочется втиснуть в lambda несколько действий через вложенные тернарные операторы и and/or, это сигнал вынести код в обычную функцию.
Частые ошибки
return или присваивание внутри lambda
f = lambda x: return x
# → SyntaxError: invalid syntax
В lambda допустимо только выражение. return, if как инструкция, for, try, присваивание через = — синтаксические ошибки.
Переменная цикла в lambda: все функции возвращают одно значение
funcs = [lambda: i for i in range(3)]
print([f() for f in funcs]) # → [2, 2, 2]
lambda запоминает не значение i, а саму переменную. К моменту вызова цикл закончился и i равно 2. Зафиксируйте текущее значение через параметр по умолчанию — он вычисляется в момент создания функции:
funcs = [lambda i=i: i for i in range(3)]
print([f() for f in funcs]) # → [0, 1, 2]
Та же ловушка встречается с обработчиками кнопок, созданными в цикле.
Забыли вызвать или передали результат вызова
print(double) для double = lambda x: x * 2 выведет <function <lambda> at 0x...>, а не число — нужен вызов double(3). Обратная ошибка — sorted(words, key=len(w)): в key передаётся функция, а не результат её вызова.
Запятая после выражения
f = lambda x: x + 1, x + 2
Python читает эту строку как кортеж из lambda и значения x + 2. Если x не определён, получите NameError. Чтобы вернуть два значения, возьмите их в скобки: lambda x: (x + 1, x + 2).
Частые вопросы
Можно ли вызвать print внутри lambda?
Можно: print() — вызов функции, то есть выражение. Сама lambda при этом вернёт результат print(), то есть None:
log = lambda msg: print("LOG:", msg)
result = log("старт") # → LOG: старт
print(result) # → None
lambda работает быстрее, чем def?
Нет. Обе записи компилируются в одинаковый байт-код, скорость вызова не различается. Выбирайте по читаемости: короткая функция на месте — lambda, всё остальное — def.
Можно ли написать рекурсивную lambda?
Технически да, если присвоить lambda имени и обращаться к нему в теле: fact = lambda n: 1 if n <= 1 else n * fact(n - 1), fact(5) вернёт 120. Но такая запись нарушает рекомендацию PEP 8 о присваивании lambda, и рекурсию понятнее писать через def.
Бывает ли асинхронная lambda?
Нет, записи async lambda в Python нет — это SyntaxError. Для корутины объявите функцию через async def. lambda может вернуть уже созданную корутину, например lambda: fetch(url), если вызывающий код сам её дождётся через await.
Можно ли передать lambda в default поля модели Django?
Нельзя: миграции Django не умеют сериализовать lambda, и makemigrations завершится ошибкой. Объявите обычную функцию на уровне модуля и передайте её без скобок: default=default_tags. Про поля моделей — в статье о моделях Django.
Итог
lambda параметры: выражение — компактный способ создать одноразовую функцию прямо в месте использования: в key для sorted(), min(), max(), в map() и filter(). Если функция нужна повторно, содержит несколько шагов или заслуживает имени — пишите def. Остальной синтаксис языка собран в шпаргалке по синтаксису Python, а функции, которые принимают и возвращают функции, разобраны в статье о декораторах.
- #Python
- #lambda
- #Функции
- #sorted
- #Синтаксис