Lambda в Python: функция в одну строку
lambda — правило без имени: аргументы до двоеточия, одно выражение после. Учимся писать короткие правила и передавать их в другие функции аргументом.
Редакция Питоники
Все функции до этого урока объявлялись def с именем и многострочным телом. Но иногда правило совсем короткое: «умножь на два», «возьми длину», «переведи в верхний регистр». Писать для него трёхстрочную def — как завозить один конверт грузовиком. Для коротких правил у Python есть lambda — функция без имени, в одну строку.
Что такое lambda в Python: коротко
lambda — это функция без имени, записанная в одну строку: слева от двоеточия перечисляются аргументы, справа — одно выражение, значение которого возвращается само. Её не создают, чтобы хранить, а передают аргументом туда, где нужно правило: sorted, map, filter. Отдельная def нужна, когда правилу требуется имя или тело длиннее одного выражения.
Правило без имени
Сравним две записи одного и того же правила «умножь на два»:
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 в переменную почти не сохраняют — её настоящее применение впереди.
Несколько аргументов
Аргументов может быть сколько нужно — через запятую до двоеточия:
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 — это по-прежнему одно выражение, а не инструкция:
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 не помещается:
# Так не запускается: в lambda тело — выражение, return не нужен
f = lambda x: return x * 2
# Так не запускается: тело lambda обязано быть одним выражением на той же строке
f = lambda x:
y = x * 2
return y
Второе ограничение — про печать. Функцию нельзя напечатать осмысленно: вместо кода выведется служебная запись с адресом памяти:
# Так не делают: печать функции даёт служебную запись
double = lambda x: x * 2
print(double)
print(double(5))
lambda или def: правило выбора
| def | lambda | |
|---|---|---|
| Имя | Обязательное | Нет — анонимная функция |
| Тело | Любое число инструкций | Одно выражение |
| Строки кода | Несколько | Одна |
| Типичное место | Логика программы | Правило-аргумент для другой функции |
| Возврат | 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), просто без имени и без тела из нескольких строк. Проверить легко — посмотрите на тип:
f = lambda x: x + 1
print(type(f))
print(callable(f))
<class 'function'> True
Отсюда следуют две вещи. Первая: lambda можно положить в переменную, список, словарь, передать аргументом, вернуть из другой функции — обращаться с ней можно как с любой функцией. Вторая: у lambda есть __name__, но там лежит строка <lambda> вместо имени. В трассировке и в отладке вы увидите <lambda> — и это сигнал, что функции не хватает имени.
lambda в цикле: все функции помнят одно и то же
Отдельная ловушка, на которой мы потеряли вечер. Если собрать несколько lambda в цикле и потом вызвать их, все они вернут одно и то же значение — последнее. Потому что lambda не копирует переменную цикла, а ссылается на неё, и к моменту вызовов переменная уже дошла до конца.
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))
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))?
Дан список цен prices = [100, 250, 80]. С помощью готовой функции apply_all и lambda получите список цен с наценкой 50 процентов — умножив каждую на 1.5 — и напечатайте его.
Что такое 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-канал или свой блог — так о проекте узнают новые читатели.
Что дальше
Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.
Что читать дальше
Функции · Урок 3
Return в Python: функция возвращает результат
print показывает значение человеку, return отдаёт программе. Учимся возвращать результат, класть его в переменную и использовать в выражениях — и не путать return с print.
Функции · Урок 7
Функция map в Python: обработка списка
map применяет функцию к каждому элементу списка одной строкой. Разбираем ленивый итератор, обёртку list() и правило выбора между map и циклом.
Функции · Урок 9
sorted с key в Python: сортировка по правилу
Порядок по своему правилу: слова по длине, пары по цене, участники по очкам. Разбираем sorted, параметр key, reverse и стабильность Timsort.
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
re · Урок 7
re.sub и re.split: замена и разрезание текста по шаблону
Замена по шаблону со ссылками на группы, функция вместо строки замены и разрезание строки по любому разделителю — два инструмента модуля re, которые заменяют десятки строк с if.
разделить строку по нескольким разделителямфункция в re.sub
json · Урок 19
Мини-клиент API на функциях
URL, разбор ответа, фильтр, файл — каждый шаг уже умещается в одну функцию. Собираем их в клиент, который читается как сценарий.
python клиент api функциифункции python для обработки json
Функции · Урок 11
**kwargs в Python: именованные на подхвате
Позиционные аргументы умеет собирать звёздочка — а какие именованные прислал вызов, заранее не знал никто. Разбираем двойную звёздочку и словарь настроек.
конфиг функция pythonpython функции уроки