Лямбда-функции в Python: синтаксис, примеры и ограничения

Лямбда-функции в Python: синтаксис, примеры и ограничения Полезное

Лямбда-функция в Python — это функция, созданная выражением lambda аргументы: выражение: у нее нет собственного имени, тело состоит ровно из одного выражения, а его значение возвращается автоматически, без return. Чаще всего лямбду передают прямо в другую функцию — в sorted, max, map, filter — как короткое правило «что сделать с каждым элементом».

Ниже — синтаксис, рабочие примеры, частые ошибки и честный ответ на вопрос, когда лямбда хуже обычного def. Все примеры проверены на Python 3.14.7, 24.09.2026.

Три термина, которые легко спутать

  • lambda-выражение — сам синтаксис lambda x: x * x в исходном коде.
  • объект функции — то, что это выражение создает при выполнении. Он того же типа function, что и результат def.
  • имя — переменная, которая ссылается на объект. У def имя есть всегда, у лямбды атрибут __name__ равен <lambda>, даже если объект сохранили в переменную.

Отсюда главное: лямбда — не «особый вид функций», а короткий способ создать обычную функцию с одним выражением внутри.

Минимальный рабочий пример

square = lambda x: x * x
print(square(4))
print(type(square), square.__name__)

def square_def(x):
    return x * x

print(type(square_def), square_def.__name__)

Вывод:

16
<class 'function'> <lambda>
<class 'function'> square_def

Обе функции считают одно и то же и имеют один тип. Разница только в имени и в том, что внутри def можно написать несколько инструкций. Присваивать лямбду переменной, как в первой строке, допустимо для демонстрации, но в рабочем коде PEP 8 советует в таком случае писать def — об этом ниже.

Синтаксис: что можно указать в аргументах и теле

Список аргументов у лямбды такой же гибкий, как у def: значения по умолчанию, *args, именованные аргументы, **kwargs. Аргументов может не быть вовсе.

f = lambda a, b=10, *args, scale=1, **kw: (a + b + sum(args)) * scale
print(f(1), f(1, 2, 3, 4), f(1, scale=2))

hello = lambda: "hi"
print(hello())

sign = lambda x: "плюс" if x > 0 else ("ноль" if x == 0 else "минус")
print([sign(v) for v in (5, 0, -3)])
11 10 22
hi
['плюс', 'ноль', 'минус']

Условие внутри лямбды пишется условным выражением A if условие else B (это выражение, а не инструкция if) или логикой and/or. Вложенные тернарные выражения работают, но уже на двух уровнях читаются плохо.

Граница тела — «одно выражение». Что это значит на практике:

Конструкция В теле лямбды Комментарий
арифметика, вызовы функций, срезы да lambda s: s.strip().lower()
A if cond else B да условное выражение
генератор списка [... for ...] да это тоже выражение
:= (моржовый оператор) да, в скобках lambda x: (y := x + 1) * y вернет 9 для x=2
return, if:, for:, try: нет это инструкции
присваивание y = ... нет инструкция
аннотации типов аргументов нет синтаксис лямбды их не поддерживает

Вызов print внутри лямбды разрешен — это вызов функции, то есть выражение. Лямбда вернет то, что вернул print, то есть None.

Лямбда как ключ в sorted, min и max

Самый частый и оправданный случай — аргумент key. Функция-ключ вызывается один раз для каждого элемента, и сравниваются уже ее результаты.

students = [("Оля", 91), ("Иван", 78), ("Анна", 91), ("Петр", 85)]

print(sorted(students, key=lambda s: s[1]))
print(sorted(students, key=lambda s: (-s[1], s[0])))
print(max(students, key=lambda s: s[1]))
[('Иван', 78), ('Петр', 85), ('Оля', 91), ('Анна', 91)]
[('Анна', 91), ('Оля', 91), ('Петр', 85), ('Иван', 78)]
('Оля', 91)

Что здесь видно. Первая строка сортирует по баллу по возрастанию; у Оли и Анны баллы равны, и они остались в исходном порядке — сортировка в Python устойчивая. Вторая строка возвращает кортеж: сначала балл по убыванию (минус работает только для чисел), при равенстве — имя по алфавиту. max при нескольких одинаковых максимумах возвращает первый встреченный.

Регистронезависимая сортировка строк — частая задача, где лямбда не обязательна:

words = ["banana", "apple", "Cherry"]
print(sorted(words))
print(sorted(words, key=lambda w: w.casefold()))
print(sorted(words, key=str.casefold))
['Cherry', 'apple', 'banana']
['apple', 'banana', 'Cherry']
['apple', 'banana', 'Cherry']

Без ключа заглавная C идет раньше строчных букв. Вторая и третья строки дают одинаковый результат: если лямбда лишь вызывает готовый метод или функцию, можно передать ее напрямую. Для выбора элемента кортежа или атрибута есть operator.itemgetter и operator.attrgetter: sorted(students, key=itemgetter(1)) делает то же, что первая строка примера выше.

map, filter и reduce

map применяет функцию к каждому элементу, filter оставляет элементы, для которых функция вернула истинное значение, functools.reduce сворачивает последовательность в одно значение.

from functools import reduce

nums = [1, 2, 3, 4, 5, 6]

m = map(lambda n: n * 10, nums)
print(m)
print(list(m))
print(list(m))

print(list(filter(lambda n: n % 3 == 0, nums)))
print(reduce(lambda acc, n: acc * n, nums))
print(reduce(lambda acc, n: acc + n, [], 0))
<map object at 0xf672daa27b00>
[10, 20, 30, 40, 50, 60]
[]
[3, 6]
720
0

Разбор по строкам:

  1. В Python 3 map и filter возвращают не список, а ленивый итератор. Отсюда <map object at ...> вместо чисел (адрес в выводе будет другим).
  2. Итератор проходится один раз: второй list(m) дает пустой список.
  3. filter оставил числа, делящиеся на 3. Исходный список не изменился.
  4. reduce перемножил все элементы: 1·2·3·4·5·6 = 720.
  5. Третий аргумент reduce — начальное значение. Без него на пустом списке будет TypeError, с ним вернется 0.

Те же задачи часто короче и понятнее решаются генератором списка и встроенными функциями:

Задача С лямбдой Без лямбды
преобразовать каждый элемент list(map(lambda n: n * 10, nums)) [n * 10 for n in nums]
отфильтровать list(filter(lambda n: n % 3 == 0, nums)) [n for n in nums if n % 3 == 0]
сумма reduce(lambda a, n: a + n, nums) sum(nums)
произведение reduce(lambda a, n: a * n, nums) math.prod(nums)
убрать «пустые» значения filter(lambda v: v, data) filter(None, data)

map с лямбдой уместен, когда функция уже есть и ее не нужно оборачивать (map(int, строки)), или когда нужен именно ленивый поток для больших данных. В остальных случаях генератор списка обычно читается лучше.

Ловушка: лямбды в цикле и позднее связывание

Лямбда запоминает не значение переменной, а саму переменную. Значение берется в момент вызова, а не в момент создания.

funcs = [lambda: i for i in range(3)]
print([g() for g in funcs])
[2, 2, 2]

Ожидали [0, 1, 2], но к моменту вызова цикл закончился, и i равно 2 для всех трех функций. Исправление — зафиксировать значение аргументом по умолчанию: он вычисляется один раз, при создании лямбды.

funcs = [lambda i=i: i for i in range(3)]
print([g() for g in funcs])
[0, 1, 2]

Альтернатива без трюка с аргументом — functools.partial. Та же ловушка касается и def внутри цикла: это свойство замыканий Python, а не только лямбд.

Когда лямбду лучше не использовать

Критерий lambda def
тело одно выражение любые инструкции
имя в трассировке ошибки <lambda> настоящее имя
docstring и аннотации типов нет есть
try/except, циклы, несколько шагов нет да
сериализация pickle (например, для multiprocessing) не работает работает для функций уровня модуля
передать короткое правило «на месте» удобно нужно отдельное определение

Имя в трассировке — не мелочь. Вот как выглядит ошибка внутри лямбды:

div = lambda a, b: a / b
div(1, 0)
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 1, in <lambda>
ZeroDivisionError: division by zero

Когда таких лямбд в модуле десяток, строка in <lambda> мало что говорит. А попытка передать лямбду в процесс через pickle заканчивается ошибкой _pickle.PicklingError: Can't pickle <function <lambda> at ...>: it's not found as __main__.<lambda>.

Практическое правило: лямбда уместна, если она короткая, используется в одном месте и передается как аргумент. Если лямбду хочется назвать (f = lambda ...), в ней больше одного условия или ее нужно тестировать отдельно — пишите def. Это рекомендация PEP 8 и линтеров (правило E731 в pycodestyle и ruff), а не требование интерпретатора: код с присвоенной лямбдой работает.

Если не получилось: частые ошибки

SyntaxError: cannot assign to lambda. Причина — присваивание в теле: f = lambda x: y = x + 1. Интерпретатор читает это как попытку присвоить значение самой лямбде. Решение — вернуть выражение lambda x: x + 1 или перейти на def.

SyntaxError: invalid syntax у слова return или if. В теле написана инструкция: lambda x: return x, lambda x: if x > 0: x. Нужно lambda x: x и lambda x: x if x > 0 else 0. Та же ошибка, если выражение после двоеточия пропущено: lambda x:.

SyntaxError: Lambda expression parameters cannot be parenthesized. Аргументы взяты в скобки: lambda (x): x. Скобки вокруг параметров лямбды не ставятся.

TypeError: <lambda>() missing 1 required positional argument: 'b'. Лямбду с двумя аргументами вызвали с одним. Проверьте число аргументов: sorted и map с одной последовательностью передают в функцию ровно один элемент.

Вместо списка печатается <map object at ...> или <filter object ...>. Это итератор — оберните в list(). Если после первого прохода результат пустой, итератор уже исчерпан: сохраните список в переменную.

Все функции из цикла возвращают одно и то же. Это позднее связывание — добавьте аргумент по умолчанию lambda i=i: ....

Выводы

  • Лямбда-функция в Python — обычный объект function, созданный одним выражением; результат выражения возвращается без return.
  • Главное применение — короткий аргумент key для sorted, min, max и функция для map/filter; если лямбда лишь вызывает готовую функцию, передавайте функцию напрямую.
  • В Python 3 map и filter возвращают одноразовые итераторы; для простых преобразований генератор списка часто понятнее.
  • В теле нельзя писать инструкции (return, if:, присваивание, try), а переменные цикла подставляются в момент вызова.
  • Если лямбде нужно имя, docstring, обработка ошибок или несколько шагов, лучше def.

Где применяется / связь с практикой

Освойте тему на практике

Лямбды постоянно встречаются в реальном коде: ключи сортировки отчетов, обработчики событий в GUI, apply в pandas, колбэки в тестах. Уверенно ими пользоваться помогает понимание функций как объектов, замыканий и итераторов — это основа, на которой строится дальнейшее изучение языка. Разобрать эти темы с практикой и проверкой кода преподавателем можно на курсе «Python-разработчик. Базовый уровень», а посмотреть формат обучения — на бесплатных открытых уроках Otus.

FAQ

Можно ли вызвать лямбду сразу, без переменной?
Да: (lambda x: x * 2)(5) вернет 10. В рабочем коде так почти не пишут — проще сразу записать выражение 5 * 2.

Быстрее ли лямбда, чем обычная функция?
Нет, при вызове они работают одинаково: обе создают объект того же типа function. Разница в скорости появляется из-за выбора между map с лямбдой и генератором списка, и ее стоит измерять на своих данных.

Может ли лямбда вернуть несколько значений?
Она возвращает одно значение, но им может быть кортеж: lambda x: (x, x * x) вернет пару, которую можно распаковать a, b = f(3).

OTUS Журнал
Скидка 5% 12-19 октября на курсы (popup)
Бесплатные открытые уроки (поп-ап)