В прошлый раз мы рассказывали о том, что такое лямбда-функции и чем они полезны в программировании. Сегодня сделаем второй заход и рассмотрим несколько ситуаций с использованием лямбда-функций в Python. Заодно выясним, когда они могут выручить, а когда можно и без них.
Что такое лямбда-функции
Лямбда-функции — это анонимные функции, которые создаются с помощью ключевого слова lambda. Их используют, когда нужно определить небольшую функцию на месте и применить её разово. Стандартный синтаксис лямбда-функции такой:
lambda переменные: значение функции
У лямбда-функций есть ограничения:
- Значение должно быть записано в одну строку (внутри не может быть циклов, других функций и всего остального, что требует для записи несколько строк).
- Функция обязательно возвращает какое-то значение.
- Это простое выражение, в котором нет присваивания, поэтому оно ничего не хранит, а лишь считает.
Теперь — к реальным примерам.
Использование в функциях высшего порядка
Функция высшего порядка — это функция, которая принимает другую функцию в качестве аргумента или возвращает её как результат. Например, у нас есть числовое значение и мы хотим применить к нему несколько математических операций: возвести в квадрат, куб и умножить на 2. Без лямбда-функции можно сделать это так:
def square(x):
return x ** 2
def cube(x):
return x ** 3
def doubling(x):
return x * 2
def apply_function(x, func):
return func(x)
result_square = apply_function(5, square)
result_cube = apply_function(5, cube)
result_double = apply_function(5, doubling)
print(result_square)
print(result_cube)
print(result_double)
Что мы здесь делаем:
- Определяем функцию
square
для возведения в квадрат. - Определяем функцию
cube
для возведения в куб. - Определяем функцию
doubling
для умножения на 2. - Указываем, с помощью какой функции посчитать квадрат.
- Указываем, с помощью какой функции посчитать куб.
- Указываем, с помощью какой функции посчитать умножение на 2.
А вот что будет с лямбда-функцией:
def apply_function(x, func):
return func(x)
result_square = apply_function(5, lambda x: x ** 2)
result_cube = apply_function(5, lambda x: x ** 3)
result_double = apply_function(5, lambda x: x * 2)
print(result_square)
print(result_cube)
print(result_double)
Что здесь происходит:
- Определяется функция
apply_function
для применения различных операций к числу. - Лямбда-функция
lambda x: x ** 2
принимает одно значение x и возвращает его квадрат (x2
). - Лямбда-функция
lambda x: x ** 3
принимает одно значение x и возвращает его куб (x³
). - Лямбда-функция
lambda x: x * 2
принимает одно значение x и возвращает его умножение на 2 (x*2
).
Создание словаря с функциями
Предыдущий пример можно реализовать ещё изящнее с помощью лямбда-функций в словаре. Представим, что нам нужно складывать, вычитать, умножать и делить два числовых значения. Для этого можно создать словарь, в котором ключами будут строки из команд или операций, а значениями — лямбда-функции, которые выполняют соответствующие действия. После создания такого словаря можно легко вызывать нужные функции по ключам:
operations = {
'add': lambda x, y: x + y,
'subtract': lambda x, y: x - y,
'multiply': lambda x, y: x * y,
'divide': lambda x, y: x / y if y != 0 else 'Ошибка деления на ноль!'
}
a, b = 10, 5
operation = 'multiply'
result = operations[operation](a, b)
print(result)
Что здесь происходит:
- Создаётся словарь operations, в котором ключи — это строки из названий операций add, substract, multiply и divide, а значения — лямбда-функции с соответствующими формулами.
- Определяются переменные a и b со значениями 10 и 5 соответственно.
- Выбираем, какую из операций в словаре хотим выполнить с этими переменными.
Использование с функцией map
Функцию map
применяют, чтобы преобразовать элементы какой-то последовательности с использованием другой функции. Например, у нас есть список чисел, каждое из которых нужно возвести в квадрат. Без лямбда-функции это можно сделать так:
def square(x):
return x ** 2
numbers = [1, 2, 3, 4, 5]
squared = list(map(square, numbers))
print(squared)
Что мы здесь делаем:
- Определяем функцию, которая принимает одно значение x и возвращает его квадрат.
- Передаём функцию square в map, чтобы она применилась к каждому элементу списка.
- Преобразуем результат в список, сохраняя его в переменной squared.
А вот как сокращается код, если использовать лямбда-функцию:
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x ** 2, numbers))
print(squared)
Что здесь происходит:
- Функция map применяет функцию, заданную лямбда-выражением
lambda x: x ** 2
, к каждому элементу списка. - Лямбда-функция
lambda x: x ** 2
принимает одно значениеx
и возвращает его квадрат. - Результат преобразуется в список и сохраняется в переменной
squared
.
Использование с функцией filter
Функция filter применяется для фильтрации элементов последовательности с использованием другой функции. Например, мы работаем с данными и нам нужно быстро отфильтровать список с идентификаторами пользователей, отделив от него чётные. Без лямбда-функции нам потребуется такой код:
def is_even(x):
return x % 2 == 0
user_ids = [101, 202, 303, 404, 505, 606, 707, 808, 909, 1000]
even_user_ids = list(filter(is_even, user_ids))
print(even_user_ids)
Что мы здесь делаем:
- Определяем функцию
is_even
, которая принимает одно значениеx
и возвращаетTrue
, если оно чётное. - Передаём функцию
is_even
вfilter
, чтобы она применялась к каждому элементу списка. - Преобразуем результат в список, сохраняя его в переменной
even_user_ids
.
А вот как сделать то же самое с лямбда-функцией:
user_ids = [101, 202, 303, 404, 505, 606, 707, 808, 909, 1000]
even_user_ids = list(filter(lambda x: x % 2 == 0, user_ids))
print(even_user_ids)
Что здесь происходит:
- Функция filter применяет функцию, заданную лямбда-выражением
lambda x: x % 2 == 0
, к каждому элементу списка, возвращая только те элементы, для которых эта функция возвращаетTrue
. - Лямбда-функция
lambda x: x % 2 == 0
принимает одно значениеx
и возвращаетTrue
, еслиx
чётное. - Результат преобразуется в список и сохраняется в переменной
even_user_ids
.
Сортировка списка кортежей
С помощью сортировки можно сортировать данные по определённому критерию. Например, у нас есть кортеж с идентификатором и названием продуктов, и нам нужно отсортировать этот набор данных по названию продукта. Без лямбда-функции код будет таким:
def get_second_element(tuple):
return tuple[1]
products = [(101, 'apple'), (303, 'banana'), (202, 'cherry')]
sorted_products = sorted(data, key=get_second_element)
# выводим результат на экран
print(sorted_products)
Что мы здесь делаем:
- Определяем функцию
get_second_element
, которая принимает кортеж и возвращает его второй элемент. - Передаём функцию
get_second_element
вsorted
как элемент сортировки. - Делаем сортировку на основе второго элемента каждого кортежа и сохраняем результат в переменной
sorted_products
.
А вот как упростить код с лямбда-функцией:
products = [(101, 'apple'), (303, 'banana'), (202, 'cherry')]
sorted_products = sorted(products, key=lambda x: x[1])
print(sorted_products) # [(101, 'apple'), (303, 'banana'), (202, 'cherry')]
Что здесь происходит:
- Функция
sorted
сортирует элементы списка по ключу, заданному лямбда-выражением lambda x: x[1]. - Лямбда-функция lambda x: x[1] принимает кортеж x и возвращает его второй элемент (индекс 1).
- Выполняется сортировка на основе второго элемента каждого кортежа, результат сохраняется в
sorted_products
.
Использование с Tkinter
С помощью библиотеки Tkinter можно создавать графические интерфейсы с различными элементами, например кнопками. При этом каждую кнопку чаще всего нужно описать только один раз. Представим, что мы создаём таск-трекер — приложение для отслеживания задач. В приложении должна быть кнопка «Добавить задачу», которая при нажатии выводит в консоль сообщение «Задача добавлена». Без лямбда-функции мы можем сделать это так:
import tkinter as tk
def on_button_click():
print("Задача добавлена")
root = tk.Tk()
button = tk.Button(root, text="Добавить задачу", command=on_button_click)
button.pack()
root.mainloop()
Что мы здесь делаем:
- Импортируем библиотеку Tkinter.
- Определяем функцию on_button_click, которая выводит текст «Задача добавлена» при нажатии кнопки.
- Создаём главное окно приложения.
- Добавляем кнопку tk.Button с подписью «Добавить задачу».
- Отображаем кнопку в главном окне.
- Запускаем основной цикл обработки событий.
А вот как можно сделать такое же с лямбда-функцией:
import tkinter as tk
root = tk.Tk()
button = tk.Button(root, text="Задача добавлена", command=lambda: print("Добавить задачу"))
button.pack()
root.mainloop()
Что здесь происходит:
- Импортируется библиотека Tkinter.
- Создаётся главное окно приложения.
- Создаётся кнопка с текстом «Добавить задачу». Аргумент
command
задаёт функцию, которая будет выполнена при нажатии кнопки. - Кнопка отображается в главном окне.
- Запускается основной цикл обработки событий.
Так лучше с лямбда-функциями или без них?
На самом деле, использовать лямбда-функции не обязательно, но они дают множество преимуществ:
- Помогают не засорять код дополнительными именованными функциями, которые больше нигде не используются.
- Позволяют не объявлять явно небольшие одноразовые функции, что делает код короче и менее громоздким.
- Делают код более читаемым, ведь вся логика видна в строке использования функции.