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

Это анонимные функции в Python, которые еще иногда называют просто — лямбды. Их основной синтаксис выглядит так: lambda parameters: expression. Используя ключевое слово lambda, мы указываем на то, что собираемся объявить лямбда-функцию. Затем следует перечисление соответствующих параметров, число которых может быть от нуля до множества. После двоеточия мы указываем выражение, которое будет вычислять лямбда-функция.

>>> # Объявление лямбда-функции 
>>> triple = lambda x: x*3
>>> 
>>> # Проверка типа 
>>> type(triple)
<class 'function'>
>>> 
>>> # Вызов лямбда-функции
>>> triple(5)
15

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

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

1. Сортировка последовательности данных 

Первый допустимый вариант использования — сортировка последовательности данных при помощи встроенной функции sorted(). Если вы знакомы с методами списков объектов, то, скорее всего, вам известен похожий метод sort(), который может сортировать объекты в списке в нужном порядке. Функция sorted() используется во многих ситуациях. 

>>> # Создание последовательности данных 
>>> grades = [{'name': 'Jennifer', 'final': 95},
...     {'name': 'David', 'final': 92},
...     {'name': 'Aaron', 'final': 98}]
>>> 
>>> # Сортировка по имени 
>>> sorted(grades, key=lambda x: x['name'])
[{'name': 'Aaron', 'final': 98}, {'name': 'David', 'final': 92}, {'name': 'Jennifer', 'final': 95}]
>>> 
>>> # Сортировка по итоговым оценкам в порядке убывания 
>>> sorted(grades, key=lambda x: x['final'], reverse=True)
[{'name': 'Aaron', 'final': 98}, {'name': 'Jennifer', 'final': 95}, {'name': 'David', 'final': 92}]

В выше описанном фрагменте кода мы создали две лямбда-функции в двух вызовах sorted(). В каждом из них лямбда определяет аргумент key и при каждой операции применяется к каждому элементу объекта списка. В нашем случае элементы являются словарями, и, когда нужно провести сортировку по имени, мы используем квадратную скобку, чтобы получить значение ключа name. Как видно из примера, после вызова функции sorted() список был отсортирован в правильном порядке .

2. Нахождение минимальных и максимальных значений 

Для нахождения минимального и максимального значений в наборе данных можно использовать две встроенные функции, а именно min() и max(). Принцип использования обоих методов идентичен. В большинстве случаев мы предоставляем итерируемый объект, состоящий из нескольких элементов. Задавая аргумент key, мы можем получить искомые минимальные и максимальные значения по тому же принципу, что и в случае с функцией sorted()

>>> # Создание последовательности данных 
>>> grades = [{'name': 'Jennifer', 'final': 95},
...     {'name': 'David', 'final': 92},
...     {'name': 'Aaron', 'final': 98}]
>>> 
>>> # Получение максимальной итоговой оценки 
>>> max(grades, key=lambda x: x['final'])
{'name': 'Aaron', 'final': 98}
>>> 
>>> # Получение минимальной итоговой оценки 
>>> min(grades, key=lambda x: x['final'])
{'name': 'David', 'final': 92}

В этом фрагменте кода был использован тот же самый список оценок нескольких студентов. Используя функцию max() и определяя аргумент key как lambda x: x[‘final’], мы смогли определить студента с максимальной итоговой оценкой. Аналогично используя min() и ту же самую лямбда-функцию, мы нашли студента с минимальной итоговой оценкой. 

3. Использование в качестве фабричной функции 

Вероятно, вам известно, что помимо встроенного типа словаря dict, в нашем распоряжении есть еще другой словарь под названием defaultdict. Этот класс является подклассом типа dict, который доступен в модуле collections. Конструктор defaultdict представляет собой defaultdict([default_factory[, ...]]). default_factory вызывается для создания значения при условии, что в словаре еще нет заданного ключа. 

>>> from collections import defaultdict
>>> 
>>> # Создание defaultdict 
>>> known_points = {'first': (2, 3), 'second': (4, 2)}
>>> points = defaultdict(lambda: (0, 0), known_points)
>>> 
>>> # Возврат значений 
>>> points['first']
(2, 3)
>>> points['second']
(4, 2)
>>> points['three']
(0, 0)

В данном примере мы сначала создали объект defaultdict, включающий несколько значений, предварительно уже записанных в него. Обратите внимание, что в качестве default_factory была установлена лямбда lambda: (0, 0). Таким образом, если defaultdict не имеет конкретного ключа, она вызывает лямбда-функцию и возвращает значение по умолчанию (0, 0). Из примера видно, что лямбда также может принимать нулевые аргументы.

4. Использование лямбда с функциями высшего порядка (не самый лучший способ) 

Под функциями высшего порядка подразумеваются такие функции, как map() и filter(). Важно помнить, что всё в Python является объектом, включая функции. Функции, принимающие другие функции в качестве аргументов или возвращающие их в качестве результата, называются функциями высшего порядка. 

map() принимает отображающую функцию и применяет ее к одному или более итерируемым объектам. Примечательно, что количество этих объектов должно соответствовать количеству аргументов, принимаемых отображающей функцией. filter() принимает функцию фильтрации и применяет ее к итерируемому объекту. Отметим, что в отличие от map() функция filter() может принять только один итерируемый объект, но в целом способы их использования аналогичны. Посмотрим, как можно использовать их вместе с лямбда-функциями. 

>>> # Создание списка чисел 
>>> numbers = [1, 2, 3, 4, 5]
>>> 
>>> # Создание списка чисел, каждое из которых умножается на 3 
>>> list(map(lambda x: x * 3, numbers))
[3, 6, 9, 12, 15]
>>> 
>>> # Создание списка четных чисел 
>>> list(filter(lambda x: x%2 == 0, numbers))
[2, 4]

Как показано в этом коде, мы использовали map() и filter() для выстраивания существующих списков в нужном формате. Обратите внимание, что необходимо использовать функцию конструктора list() для создания объектов list, так как возвращаемые значения map() и filter() не являются этими объектами. 

Но почему же использование лямбда-функции с функциями высшего порядка map() и filter() нежелательно? Причина в том, что в большинстве случаев для решения задач можно использовать генератор списков. 

Следовательно, те же самые результаты можно получить, преобразовав уже знакомый нам код следующим образом:

>>> # Создание списка чисел 
>>> numbers = [1, 2, 3, 4, 5]
>>> 
>>> # Создание списка чисел, каждое из которых умножается на 3  
>>> [x * 3 for x in numbers]
[3, 6, 9, 12, 15]
>>> 
>>> # Создание списка четных чисел 
>>> [x for x in numbers if x%2 == 0]
[2, 4]

Для таких случаев подходят и функции высшего порядка, и генераторы списков, а что выбрать — решать вам. Лично я отдаю предпочтение генератору списков за его лаконичность. Но некоторые тяготеют к использованию функционального подхода с применением map() и filter(), что также оправдано. 

Заключение 

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

Благодарю за внимание! 

Читайте также:


Перевод статьи Yong Cui, Ph.D.: Lambdas in Python — 4 Practical Examples

Предыдущая статья5 настроек рабочего стола Linux для разработчиков
Следующая статьяЛюбовь к программированию начинается с расширений VS Code