Перейти к содержанию

412 уроков, 25 библиотек и челлендж «Что выведет код?» — бесплатно, код прямо в браузере

Начать обучение
Урок 6 из 20 Начальный 15 мин 100 XP

Lambda в Python: функция в одну строку

lambda — правило без имени: аргументы до двоеточия, одно выражение после. Учимся писать короткие правила и передавать их в другие функции аргументом.

Редакция Питоники

Все функции до этого урока объявлялись def с именем и многострочным телом. Но иногда правило совсем короткое: «умножь на два», «возьми длину», «переведи в верхний регистр». Писать для него трёхстрочную def — как завозить один конверт грузовиком. Для коротких правил у Python есть lambda — функция без имени, в одну строку.

Что такое lambda в Python: коротко

lambda — это функция без имени, записанная в одну строку: слева от двоеточия перечисляются аргументы, справа — одно выражение, значение которого возвращается само. Её не создают, чтобы хранить, а передают аргументом туда, где нужно правило: sorted, map, filter. Отдельная def нужна, когда правилу требуется имя или тело длиннее одного выражения.

Правило без имени

Сравним две записи одного и того же правила «умножь на два»:

def против lambda
def double_def(x):
    return x * 2

double_lambda = lambda x: x * 2

print(double_def(5))
print(double_lambda(5))
Вывод
10
10

Оба вызова вернули десятку: lambda — та же функция, только записанная компактнее. Разбираем запись lambda x: x * 2 по частям:

  • lambda — ключевое слово: начинается функция-выражение;
  • x — аргументы: стоят до двоеточия, через запятую, как у def;
  • двоеточие отделяет аргументы от тела;
  • x * 2 — тело: ровно одно выражение; его значение lambda и возвращает, слово return не пишется.

Аргументы lambda стоят до двоеточия, а после двоеточия пишут одно выражение — его значение lambda и возвращает. Присваивание double_lambda = ... здесь показано для сравнения с def: так видно, что перед вами функция. В настоящем коде lambda в переменную почти не сохраняют — её настоящее применение впереди.

Несколько аргументов

Аргументов может быть сколько нужно — через запятую до двоеточия:

lambda с двумя аргументами
total = lambda price, quantity: price * quantity

print(total(150, 3))
print(total(80, 2))
Вывод
450
160

Правило «цена умножить количество» поместилось в одну строку: price и quantity — аргументы, price * quantity — выражение, чьё значение возвращается. Всё, что умела def с аргументами — позиционные вызовы, имена, умолчания — lambda тоже умеет.

Главное применение: lambda как аргумент

Настоящая сила lambda раскрывается, когда функция передаётся в другую функцию. В Python функции — полноценные значения: их можно передавать аргументами, как строки и числа. Напишем функцию apply, которая принимает список и правило — и применяет правило к каждому элементу:

правило передаётся аргументом
def apply(numbers, rule):
    result = []
    for n in numbers:
        result.append(rule(n))
    return result

print(apply([1, 2, 3, 4], lambda n: n * 10))
print(apply([1, 2, 3, 4], lambda n: n ** 2))
Вывод
[10, 20, 30, 40]
[1, 4, 9, 16]

Функция apply написана один раз, а правила приходят аргументом: в теле цикла rule(n) вызывает то, что передали — умножение на десять во втором вызове, квадрат в третьем. lambda создана прямо на месте вызова, без имени: она нужна только здесь и сейчас. Это главный способ использования lambda в реальном коде — не хранить, а передавать.

Правило может быть любым коротким выражением — работа со строками не исключение:

правила для строк
def apply_all(items, rule):
    result = []
    for item in items:
        result.append(rule(item))
    return result

words = ['кот', 'собака', 'ёж']
print(apply_all(words, lambda w: len(w)))
print(apply_all(words, lambda w: w.upper()))
Вывод
[3, 6, 2]
['КОТ', 'СОБАКА', 'ЁЖ']

В тело lambda помещается и выражение с if-else — это по-прежнему одно выражение, а не инструкция:

условие внутри lambda
def apply_all(numbers, rule):
    result = []
    for n in numbers:
        result.append(rule(n))
    return result

print(apply_all([1, 2, 3, 4], lambda n: 'чёт' if n % 2 == 0 else 'нечёт'))
Вывод
['нечёт', 'чёт', 'нечёт', 'чёт']

Выражение «значение если условие, иначе другое значение» посчиталось для каждого числа и вернуло слово. Даже с условием тело lambda остаётся одним выражением — это жёсткое ограничение.

Границы lambda

Ограничение — одно выражение. Всё, что требует инструкций: присваивания, циклы, несколько строк — в lambda не помещается:

ошибка: return в lambda не пишется
# Так не запускается: в lambda тело — выражение, return не нужен
f = lambda x: return x * 2
Python ответит SyntaxError: значение выражения после двоеточия возвращается само. return используется только в обычных функциях def.
ошибка: многострочное тело
# Так не запускается: тело lambda обязано быть одним выражением на той же строке
f = lambda x:
    y = x * 2
    return y
Python ответит SyntaxError. Нужны присваивания или циклы — пишите обычную def с полноценным телом.

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

ошибка: печать функции вместо вызова
# Так не делают: печать функции даёт служебную запись
double = lambda x: x * 2
print(double)
print(double(5))
Первая строка напечатает запись вида <function <lambda> at 0x...> с адресом памяти — у каждой машины свой, поэтому печатать функции бессмысленно. Вторая строка — вызов double(5) — вернёт 10. Функцию вызывают, а не печатают.

lambda или def: правило выбора

deflambda
ИмяОбязательноеНет — анонимная функция
ТелоЛюбое число инструкцийОдно выражение
Строки кодаНесколькоОдна
Типичное местоЛогика программыПравило-аргумент для другой функции
Возвратreturn значениеЗначение выражения автоматически

Правило выбора простое. Правилу нужно имя или тело больше одного выражения — пишите def, как во всех прошлых уроках. Правило короткое и нужно прямо сейчас, как аргумент — lambda на месте вызова. Сохранять lambda в переменную с осмысленным именем не стоит: для именованных функций есть def.

  • передача правила в функции обработки списков — map и filter из следующего урока принимают lambda первым делом;
  • сортировка по своему критерию: sorted с именованным аргументом key и lambda внутри — тема урока про sorted;
  • правила-вычисления для словарей и таблиц, когда правило умещается в выражение.

lambda освоена как правило-аргумент — теперь появились инструменты, которые такое правило ждут. В уроке про map правило применяется к каждому элементу списка одной строкой вместо цикла с append, а возврат значения lambda делают автоматически, как это делает return в обычных функциях из урока про возврат результата.

Под капотом lambda — обычная функция

Никакой отдельной сущности «лямбда» в Python нет. Это синтаксический сахар: запись lambda x: x * 2 создаёт такой же объект функции, как def double(x), просто без имени и без тела из нескольких строк. Проверить легко — посмотрите на тип:

тип объекта lambda
f = lambda x: x + 1

print(type(f))
print(callable(f))
Вывод
<class 'function'>
True

Отсюда следуют две вещи. Первая: lambda можно положить в переменную, список, словарь, передать аргументом, вернуть из другой функции — обращаться с ней можно как с любой функцией. Вторая: у lambda есть __name__, но там лежит строка <lambda> вместо имени. В трассировке и в отладке вы увидите <lambda> — и это сигнал, что функции не хватает имени.

lambda в цикле: все функции помнят одно и то же

Отдельная ловушка, на которой мы потеряли вечер. Если собрать несколько lambda в цикле и потом вызвать их, все они вернут одно и то же значение — последнее. Потому что lambda не копирует переменную цикла, а ссылается на неё, и к моменту вызовов переменная уже дошла до конца.

все три lambda вернут 2
funcs = []
for i in range(3):
    funcs.append(lambda: i)

print([f() for f in funcs])
Вывод
[2, 2, 2]

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

правильно: значение по умолчанию
funcs = []
for i in range(3):
    funcs.append(lambda i=i: i)

print([f() for f in funcs])
Вывод
[0, 1, 2]

Когда lambda становится неудобной

lambda заканчивается там, где начинается что-то одноразовое. Если правило переиспользуется, требует описания или перестаёт помещаться в строку, короткая запись мешает больше, чем помогает.

СитуацияЧто делать
Правило вызывается из трёх местНазовите его: обычная def с именем
Внутри нужно два-три шагаТолько def: в lambda помещается одно выражение
Функцию будет читать другой человекdef и docstring: у lambda нет места для описания
Правило не помещается в строкуdef с переносом строки: читаемость важнее компактности
Правило нужно отладитьdef: у lambda в трассировке нет имени, только <lambda>

Мы держимся простого правила: правило не помещается в одну строку или его нужно назвать — пишем def. lambda — это не способ писать короткий код вообще, это способ передать правило туда, где оно нужно, и забыть о нём сразу после вызова.

Практика: три правила для одного списка

Типичная задача — прогнать одни и те же данные через несколько правил. Одна функция и три lambda рядом: ни одно из правил не появляется в коде под именем.

список цен через три правила
def apply_all(items, rule):
    return [rule(item) for item in items]

prices = [100, 250, 80]

print(apply_all(prices, lambda p: p * 2))
print(apply_all(prices, lambda p: p - p // 10))
print(apply_all(prices, lambda p: p > 150))
Вывод
[200, 500, 160]
[90, 225, 72]
[False, True, False]

Третье правило отвечает на вопрос «а что осталось», а не «сколько получилось» — по списку True и False. Такие фильтры в Python вынесены в отдельную функцию: filter из урока 8 делает ровно это одной строкой, а map из следующего урока — то же, что наша apply_all, только встроенная в язык.

Что выведет код?

Сначала предскажи ответ в голове — это главный навык программиста.

f = lambda x: x + 1
print(f(2))
f = lambda a, b: a * b + 1
print(f(2, 3))
def apply(nums, rule):
    out = []
    for n in nums:
        out.append(rule(n))
    return out

print(apply([1, 2], lambda x: x * 3))
Проверь себя
0 / 9

1. Что такое lambda в Python?

2. Что возвращает lambda без слова return?

3. Что помещается в тело lambda?

4. Что вернёт вызов f(2, 3), если f = lambda a, b: a * b?

5. Как lambda используется в реальном коде чаще всего?

6. Когда вместо lambda нужно писать обычную def?

7. В цикле собрали три lambda: funcs.append(lambda: i), где i меняется от 0 до 2. Что напечатает [f() for f in funcs]?

8. Как правильно создать lambda внутри цикла, чтобы каждая помнила своё значение?

9. Что напечатает print(type(lambda x: x + 1))?

Карточки терминов
Запомнено: 0 / 8
Практика

Дан список цен prices = [100, 250, 80]. С помощью готовой функции apply_all и lambda получите список цен с наценкой 50 процентов — умножив каждую на 1.5 — и напечатайте его.

practice.py
Вопросы и ответы по уроку

Что такое lambda в Python?

Функция без имени в одну строку: lambda x: x * 2 — правило «умножь на два». Аргументы пишутся до двоеточия, после — одно выражение, чьё значение возвращается автоматически, без return.

Урок 7: функция map

Чем lambda отличается от def?

У lambda нет имени, тело — ровно одно выражение, и запись занимает одну строку. def объявляет именованную функцию с многострочным телом. Правило: нужна функция с именем или сложной логикой — def; короткое правило на месте вызова — lambda.

Какие ограничения у lambda?

Одно выражение в теле: присваивания, циклы и return не помещаются и дают SyntaxError. И печатать lambda нельзя — выведется служебная запись с адресом памяти; результат даёт только вызов со скобками.

Зачем lambda, если есть def?

lambda создают ради передачи в другие функции: apply(числа, lambda n: n * 10), sorted со своим критерием, map и filter — везде правило пишется на месте вызова без имени. Отдельная трёхстрочная def для такого правила была бы длиннее самого правила.

Можно ли сохранить lambda в переменную?

Технически да, но так не делают: теряется весь смысл. Имя, ради которого вы сохранили правило, лучше дать обычной функции через def — её потом можно вызвать, отладить, описать docstring и показать в трассировке. lambda живёт на месте вызова и исчезает вместе с ним.

Почему lambda, созданные в цикле, возвращают одинаковые значения?

Потому что lambda не копирует переменную цикла, а ссылается на неё. К моменту вызовов переменная уже дошла до конца цикла, и все функции видят одно и то же значение. Лечение — значение по умолчанию: lambda i=i: i, оно копируется в момент создания функции.

Как отличить lambda от обычной функции при отладке?

По имени: у обычной def в атрибуте __name__ лежит её имя, у lambda — строка <lambda>. В трассировке вызовов вы увидите <lambda> вместо имени — это сигнал, что функции стоит дать имя и написать обычную def.

Понравился урок? Сошлитесь на него

«Аргументы lambda стоят до двоеточия, а после двоеточия пишут одно выражение — его значение lambda и возвращает.»

Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Что дальше

Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.

Похожие уроки по темам

Подобраны автоматически по пересечению тем и ключевых слов.