Мы уже много рассказали про разные встроенные функции в языке Python. Сегодня расскажем не про какую-то конкретную, а про особый вид функций — lambda
. Это инструмент, которым удобно пользоваться для расширения возможностей кода, когда программу нужно сделать немного более функциональной, но без сильного усложнения.
Сейчас разберём, что конкретно умеют лямбда-функции и когда их стоит применять, а когда лучше подключить другие возможности.
Что такое лямбда-функции
В Python есть два вида функций: классические и безымянные лямбда-функции. В коде это проявляется так.
Классические функции обозначаются словом def
, пишутся как минимум в две строки и могут принимать аргументы в скобках. Аргументы для работы функции необязательны, но скобки должны быть:
# объявляем функцию без аргументов
def classic():
# функция выводит строку и заканчивает работу
print('Классическая функция в Python')
# для запуска пишем имя функции и скобки
classic()
Теперь, если запустить этот код, он выведет строку:
Классическая функция в Python
Такую функцию можно создать и вызывать в любом месте по её названию.
Лямбда-функции создаются прямо в месте использования, поэтому выглядят не как отдельные фрагменты, а как часть остального кода. Они обозначаются словом lambda
, пишутся в одну строку и сначала принимают аргументы, а потом, через двоеточие, единственное выражение, которое вычисляется и возвращает значение.
Самое частое и с точки зрения хорошего python-кода правильное применение таких функций — использовать их в качестве аргумента других функций. Поэтому первый пример будет выглядеть немного сложно, зато более полезно. Чтобы было проще, дальше мы всё разберём максимально подробно.
# создаём список
numbers = [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5]
# применяем встроенную в Python функцию filter().
# в аргументах мы даём ей две вещи: лямбда-функцию и созданный выше список.
# filter() проверит, соответствуют ли элементы списка заданному условия после слова lambda
filtered_numbers = filter(lambda x: x > 0, numbers)
# функция filter() возвращает объект специального типа,
# поэтому сначала мы преобразуем результат в список, а потом выводим его экран
print(list(filtered_numbers))
В этом коде у нас есть список — коллекция из нескольких значений.
В примере мы создали его сами, но в реальной программе такой список может получиться после сложной работы — например, мы получаем номера зарегистрировавшихся пользователей, которые хотят применить промокод в магазине. Если срок действия промокода истёк, номер пользователя отрицательный или равен нулю. Если ещё действует, то номер будет положительным.
Для фильтрации значений мы используем другую функцию — filter()
. Если интересно, как она работает, про неё у нас есть отдельная статья. Мы создаём условие, чтобы получить только положительные номера из списка, — и как раз для этого условия используем lambda-функцию.
Теперь после запуска мы получим отфильтрованный список:
[1, 2, 3, 4, 5]
Синтаксис лямбда-функций
Лямбда-функции не зря называют безымянными — они используют только ключевое слово lambda
и сразу начинают работать с аргументами и выражениями. Их основное предназначение — быстро подсчитать какое-то выражение и вернуть результат.
Пример лямбда-функции
Формула lambda-функции выглядит так:
lambda arguments: expression
Здесь lambda
— ключевое слово, arguments
— аргументы для работы, а expression
— выражение, которое с этими аргументами что-то делает и возвращает получившееся после этих вычислений значение.
Если взять пример синтаксиса без использования в реальном коде, то это выглядит так:
lambda x, y: x ** 2 + y ** 3
Эта функция принимает два аргумента. После этого она возводит первый аргумент в квадрат, а второй в куб. В конце складывает эти значения и возвращает эту сумму программе. И всё это в рамках одного выражения.
А если сделать так, чтобы эта лямбда-функция реально работала, то её нужно поместить внутри или другой функции, или переменной:
# объявляем переменную и присваиваем в неё lambda-функцию
anonymous = lambda x, y: x ** 2 + y ** 3
# вызываем lambda-функцию, передав ей в качестве аргументов два числа
print(anonymous(3, 4))
Здесь мы положили лямбда-функцию в переменную, и теперь её можно вызывать в любом месте кода через вызов переменной, Питон поймёт. Выглядит удобно, но делать так в реальной программе не стоит.
Почему не стоит: задача безымянных функций — использоваться в одном месте и быстро помогать вычислениям. Если вам нужна какая-то логика, которая будет задействована в разных местах программы, лучше сразу создать обычную def-функцию. Это будет соответствовать установленным правилам между Python-программистами и даст возможность масштабировать функцию в будущем при необходимости.
Масштабировать лямбда-функции сложно. Для объяснения сравним подробнее классические функции и безымянные.
Различие между лямбда-функциями и обычными функциями
Оба вида функций могут принимать неограниченное количество аргументов для работы, и в этом их единственное сходство.
Обычная функция может быть полноценной программой. Она может разделить полученные аргументы на разные группы и подсчитать их все по разным правилам. Def-функция может присваивать значения внутри и создавать переменные только на время своей работы, а может дотянуться до переменных в другом месте программы и изменить их.
Ещё такая функция может ничего не возвращать в результате работы.
Лямбда-функция ничего этого не умеет. Она принимает только одно выражение и работает с аргументами только по этому правилу. В безымянных функциях нельзя изменять переменные, которые объявлены не в ней, потому что не работает использование оператора присваивания =
или :=
.
В отличие от обычной, лямбда-функция обязательно должна возвращать какое-то значение.
Использование лямбда-функций
Лямбда-функции похожи на одноразовые инструменты: мы используем их в одном месте, где можно не создавать отдельную функцию, и больше к ним не возвращаемся. Сам фрагмент кода с лямбда-функциями может вызываться неоднократно, но важно то, что у безымянных функций есть только одна задача, которая не меняется.
Звучит сложно, поэтому посмотрим на примерах. Для этого возьмём три встроенные функции Python, которые могут использовать внутри себя lambda-функции. Все три работают с коллекциями — списками, кортежами, строками и другими наборами элементов, которые структурированы определённым образом.
Элементы коллекций можно перебирать, то есть итерироваться по элементам. Во время итерации к каждому элементу можно применять прописанные в программе действия.
Лямбда-функции с функцией map()
Map()
проходит по всем элементам коллекции и делает с ними то, что скажет разработчик.
Работает это так: map()
принимает два аргумента: какое-то выражение или функцию и итерируемую коллекцию. После этого применяет прописанное первым аргументом действие к каждому элементу, например можно умножить все числа в списке на 2 или привести символы в строке к нижнему регистру.
В качестве первого ключевого аргумента можно использовать лямбда-функцию и задать нужное правило для действия с элементами.
Этот код берёт созданный список чисел и умножает каждый элемент на 2:
# создаём список чисел
numbers = [1, 2, 3, 4]
# применяем лямбда-функцию, которая умножает каждый элемент на 2
doubled_numbers = list(map(lambda x: x * 2, numbers))
# выводим результат
print(doubled_numbers)
Если запустить эту программу, в консоли получим:
[2, 4, 6, 8]
Лямбда-функции с функцией filter()
Filter()
проверяет, соответствуют ли элементы коллекции условию. Если нет — выкидывает их из последовательности. Остаются только те, что проходят проверку.
Для этого первым аргументом функция принимает условие, которое можно обозначить через лямбда-функцию.
Вот как это выглядит:
# создаём список строк
heroes = ["Крис Адамс", "Вин Таннер", "Чико", "Бритт", "Гарри Лаки", "Ли Берн", "Lambda Python"]
# фильтруем, оставляя только имена, начинающиеся на "К"
filtered_heroes = list(filter(lambda x: x.startswith('К'), heroes))
# выводим результат
print(filtered_heroes)
При запуске мы получим только те элементы, которые начинаются на заглавную «К»:
['Крис Адамс']
Лямбда-функции с функцией reduce()
Эта функция проходит по всем элементам и тоже последовательно применяет одну инструкцию к каждому. Но при этом она сохраняет значение от каждой предыдущей операции, то есть аккумулирует его. В каждом следующем действии должны участвовать этот аккумулятор и новый элемент. В конце reduce()
вернёт итоговое единичное значение. Получается, что функция как бы сворачивает всю коллекцию до одного значения.
Что именно нужно делать с элементами и как приводить их к одному результату, можно указать через лямбда-функцию.
Пример того, как это может работать, — сложим все элементы списка:
# импортируем reduce
from functools import reduce
# создаём список чисел
numbers = [1, 2, 3, 4]
# складываем все числа через reduce и лямбда-функцию
sum_numbers = reduce(lambda a, b: a + b, numbers)
# выводим результат
print(sum_numbers)
В результате получаем одного значение:
10
Лямбда-функции и списковые включения
Списковые включения, генераторы списков или list comprehensions — это всё один и тот же инструмент для создания списков по нужному нам правилу. Один из вариантов создания правила — использовать лямбда-функцию и записать в ней инструкцию по созданию элементов списка.
Для примера сделаем так: создадим генератор, который возьмёт числа от 1 до 10 и добавит в список только те, что кратны 3:
# генерируем числа от 1 до 10 и оставляем только те, что делятся на 3
result = [x for x in range(1, 11) if (lambda x: x % 3 == 0)(x)]
# выводим результат на экран
print(result)
В терминале запуска получаем:
[3, 6, 9]
Лямбда-функции и условные операторы
В лямбда-функции можно вставить условные операторы if
и else
, если сделать это в одну строку и в рамках одного выражения. Но обычно технически проще и более правильно с точки зрения хорошего Python-кода создать условие другим способом.
Например, нам нужно оставить в списке только строки длиннее 4 символов. Мы можем сделать это с явным использованием if
и else
, тогда это будет выглядеть так:
# создаём список городов
cities = ["Москва", "Сочи", "Нью-Йорк", "Токио", "Париж", "Баку", "Рим"]
# проверяем каждый элемент на длину и возвращаем
# значение True, если длина больше 4. функция filter()
# оставляет только те значения, которые вернут True
filtered_cities = list(filter(lambda city: True if len(city) > 4 else False, cities))
# выводим результат на экран
print(filtered_cities)
Но такое условие получается довольно громоздким и не сразу понятным. Проще сделать это через сравнение >
:
# создаём список городов
cities = ["Москва", "Сочи", "Нью-Йорк", "Токио", "Париж", "Баку", "Рим"]
# оставляем только города с длиной названия > 4 символов
filtered_cities = list(filter(lambda city: len(city) > 4, cities))
# выводим результат на экран
print(filtered_cities)
Оба варианта выведут один результат:
['Москва', 'Нью-Йорк', 'Токио', 'Париж']
Лямбда-функции и множественные операторы
Лямбда-функции могут использовать только одно выражение в качестве инструкции. Если попробовать добавить несколько, мы получим ошибку.
Например, такая функция не будет работать:
# пытаемся поместить в одну функцию два выражения
function = lambda x: (print(x); x + 1)
Python позволяет добавлять выражения в одну строку, но в безымянных функциях это не предусмотрено, поэтому при запуске мы увидим такое сообщение в консоли:
f = lambda x: (print(x); x + 1)
^
SyntaxError: invalid syntax
Иногда можно пойти на хитрость: записать несколько условий как часть кортежа, через запятую. Тогда функция посчитает несколько выражений и вернёт кортеж значений.
Но для этого есть более правильный инструмент — классические def-функции. Вся красота и польза лямбда-функций в их простоте, поэтому делать их сложными без крайней необходимости не имеет смысла.
Практические примеры использования лямбда-функций
Вот ещё несколько примеров использования безымянных функций, которые могут встретиться в простых программах.
Пример с условием
У нас есть список зарегистрированных температур в градусах по Цельсию. Все показатели нужно перевести в градусы по Фаренгейту и добавить в новый список только те, что выше 20 градусов.
Если использовать операторы if
и else
, получается такой код:
# список температур в градусах Цельсия
temperatures = [25, 30, 15, 10, 35]
# преобразуем в градусы Фаренгейта и оставляем только значение выше 20 градусов
hot_temps_fahrenheit = list(
map(
# преобразуем в градусы Фаренгейта
lambda x: (x * 9/5) + 32,
filter(
# фильтруем значения ниже 20 градусов
lambda x: True if x > 20 else False,
temperatures
)
)
)
# выводим на экран
print(hot_temps_fahrenheit)
Здесь сначала мы преобразуем все значения, проходя по списку функцией map()
. После этого оставляем только показатели выше 20 градусов через filter()
.
То же самое можно сделать без применения if
и else
. Получится небольшой алгоритм:
# список температур в градусах Цельсия
temperatures = [25, 30, 15, 10, 35]
# преобразуем в градусы Фаренгейта и оставляем только значение выше 20 градусов
hot_temps_fahrenheit = list(
# преобразуем в градусы Фаренгейта
map(lambda x: (x * 9/5) + 32,
# оставляем только значения выше 20
filter(lambda x: x > 20, temperatures))
)
# выводим на экран
print(hot_temps_fahrenheit)
И первый, и второй вариант вернут там такой список:
[77.0, 86.0, 95.0]
Пример с множественными операторами
Если всё-таки нужно добавить несколько операций в лямбда-функцию, это можно сделать при помощи кортежа.
В примере ниже мы проходим по списку имён функцией map()
. Для каждого имени сначала преобразуем первую букву в заглавную, а потом добавляем в кортеж длину строки.
Получаем такую программу:
# список имён
names = ["Алиса", "Пётр", "Вячеслав"]
# преобразуем: первая буква заглавная + длина имени
use = list(map(lambda x: (x.capitalize(), len(x)), names))
# выводим на экран
print(use)
Код выводит такой результат:
[('Алиса', 5), ('Пётр', 4), ('Вячеслав', 8)]
Преимущества и недостатки лямбда-функций
После нескольких примеров видно, что лямбда-функция — это анонимный и упрощённый аналог обычной функции. Её можно вставить там, где достаточно одного выражения и если это выражение будет выполнять единственную задачу.
Плюсы лямбда-функции в её простоте и удобстве работы с другими функциями. Если нужны сложные условия в несколько строк, лучше просто взять обычную функцию.
Минусы в том, что несмотря на простоту, добавление функции прямо в строку кода всё равно усложняет чтение программы. А документацию в безымянную функцию добавить нельзя, потому что она создаётся в одну строку на месте использования.
Поэтому использовать их нужно осторожно, чтобы не усложнять работу.
Бонус для читателей
Если вам интересно погрузиться в мир ИТ и при этом немного сэкономить, держите наш промокод на курсы Практикума. Он даст вам скидку при оплате, поможет с льготной ипотекой и даст безлимит на маркетплейсах. Ладно, окей, это просто скидка, без остального, но хорошая.
Вам слово
Приходите к нам в соцсети поделиться своим мнением о статье и почитать, что пишут другие. А ещё там выходит дополнительный контент, которого нет на сайте — шпаргалки, опросы и разная дурка. В общем, вот тележка, вот ВК — велком!