lambda-выражения в Python

В Python lambda-функция — это анонимная функция, представленная в виде одного выражения. Её можно использовать вместо обычной небольшой функции.

Для иллюстрации анонимных функций создадим пример, в котором используются обычные функции, а потом перепишем его с использованием анонимных функций.

Создадим функцию, которая принимает два аргумента:

  • список слов (words);
  • функцию, которую нужно применить к каждому слову из списка words.
def edit_story(words, func):
    for word in words:
        print(func(word))

Теперь создадим список слов и напишем функцию, которую нужно к ним применить. В качестве слов возьмём список имён. Функция должна вывести на экран каждое имя с большой буквы и поприветствовать пользователя.

names = ['anna', 'alex', 'max', 'maria']

def greet(word):
    return f'Welcome, {word.capitalize()}!'

Теперь запустим всё вместе.

def edit_story(words, func):
    for word in words:
        print(func(word))

names = ['anna', 'alex', 'max', 'maria']

def greet(word):
    return f'Welcome, {word.capitalize()}!'

edit_story(names, greet)
# Welcome, Anna!
# Welcome, Alex!
# Welcome, Max!
# Welcome, Maria!

Функция greet() очень короткая. Мы можем заменить её lambda-выражением:

# используем уже объявленные выше edit_story и names
edit_story(names, lambda name: f'Welcome, {name.capitalize()}!')
# Welcome, Anna!
# Welcome, Alex!
# Welcome, Max!
# Welcome, Maria!

lambda-выражение имеет ноль или более аргументов, разделённых запятой, за которыми следует двоеточие (:), а за ним выражение, результат которого возвращается при вызове функции.

lambda arg1, arg2, ..., argN: выражение, использующее аргументы

В нашем примере мы передаём lambda-выражению один аргумент name.

Аргументам lambda можно присваивать значения по умолчанию:

f = lambda x=10, y=10, z=10: x + y + z
f()  # 30

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

  • lambda — это выражение, а не оператор. По этой причине оно может находиться в тех местах, где def синтаксически не разрешено, например, внутри спискового литерала или в аргументах вызова функции;
  • оператор def создаёт объект функции и связывает имя с этим объектом. Выражение lambda возвращает значение (новую функцию). При желании это значение можно присвоить имени;
  • тело lambda является одиночным выражением, а не блоком операторов. Например, мы не можем поместить в тело lambda блок операторов if или цикл for, присваивание через =, return и т.п.

Пример, где демонстрируется, что результат выражения lambda можно присвоить имени. Создадим две функции с одинаковым поведением.

def func(x, y, z):
    return x + y + z

f = lambda x, y, z: x + y + z

func(4, 5, 6)  # 15
f(4, 5, 6)     # 15

Код в теле lambda следует тем же самым правилам поиска в областях видимости, что и код внутри def (правило LEGB). lambda-выражение создаёт функцию со своей локальной областью видимости. Эта область видимости работает по тем же правилам LEGB, что и область видимости обычной функции.

def farewell():
    intro = "Bye bye"
    return lambda name: print(f'{intro}, {name}!')

bye = farewell()
bye('Anna')
# Bye bye, Anna!

В примере выше lambda берёт переменную intro из области видимости объемлющей функции farewell.

Это поведение можно использовать для создания функций, которые сохраняют доступ к переменным из области видимости объемлющей функции.

def user_says(user_name):
    return lambda phrase: print(f'{user_name} says: "{phrase}"')

maria_says = user_says("Maria")
anna_says = user_says("Anna")

maria_says("Hello everyone")
# Maria says: "Hello everyone"
anna_says("Python is awesome")
# Anna says: "Python is awesome"

В примере выше каждый вызов user_says() создаёт отдельную область видимости, поэтому функции maria_says и anna_says используют разные значения user_name.

Строго говоря, пример выше можно ещё сократить, вложив lambda внутрь lambda (да, lambda-выражения поддерживают вложенность).

user_says = lambda user_name: lambda phrase: print(f'{user_name} says: "{phrase}"')

alex_says = user_says('Alex')
alex_says('What is going on here?')
# Alex says: "What is going on here?"

Внимание! Код выше приведён исключительно для ознакомления с тёмным миром мудрёных выражений. Автор статьи не несёт ответственности за ту ругань, которая полетит в вашу сторону на код-ревью, если вы будете применять подобные конструкции.

Когда использовать lambda

Использовать lambda-выражения совершенно необязательно. Всегда можно вместо них использовать более привычный оператор def. Чаще всего именно это и требуется, так как из-за синтаксических ограничений lambda-выражения либо не работают, либо становятся малопонятными. Но в сценариях, где нужно всего лишь вставить небольшие порции исполняемого кода в места их применения, выражение lambda может сделать код проще и короче.

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

def lower_full(x, y):
    if x < y:
        return x
    return y


lower_short = lambda x, y: x if x < y else y

print(lower_full(10, 20))   # 10
print(lower_short(10, 20))  # 10

Примечание. Ранее я упоминал, что в lambda нельзя использовать оператор if. Однако в Python условное выражение x if condition else y является не оператором, а выражением, и оно допустимо в lambda.

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

users = [
    {'name': 'Anna', 'age': 25},
    {'name': 'Alex', 'age': 28},
    {'name': 'Maria', 'age': 14},
]

sorted_users = sorted(users, key=lambda user: user['age'])

for user in sorted_users:
    print(user)
# {'name': 'Maria', 'age': 14}
# {'name': 'Anna', 'age': 25}
# {'name': 'Alex', 'age': 28}

Рецепт рефакторинга lambda-выражений

Если lambda-выражение читается с трудом, то настоятельно рекомендуется проследовать совету Фредрика Лундха:

  1. напишите комментарий, объясняющий, что делает lambda;
  2. внимательно изучите этот комментарий и придумайте имя, в котором заключалась бы суть изложенного в нём;
  3. преобразуйте lambda в def, указав придуманное имя;
  4. удалите комментарий.

Совет взят отсюда.

Источники:

  1. Билл Лобанович. "Простой Python. Современный стиль программирования". Питер, 2024 г.
  2. Марк Лутц. "Изучаем Python" 5-е изд. Диалектика, 2020 г.
  3. Лусиану Ромальо. "Python. К вершинам мастерства. Лаконичное и эффективное программирование". 2-е изд. ДМК, 2022 г.
  4. https://docs.python.org/