В этом уроке:

В синтаксис Python и других языков, таких как Java, C# и даже C++, лямбда-функции были добавлены, тогда как в таких языках, как LISP или семейство языков ML, Haskell, OCaml и F#, лямбда-выражения используются в качестве основной концепции.

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

К концу этой статьи вы будете знать:

  • Как появились лямбды в Python;
  • Как лямбды сравниваются с обычными функциональными объектами;
  • Как писать лямбда-функции;
  • Какие функции в стандартной библиотеке Python используют лямбды;
  • Когда использовать или избегать лямбда-функций Python.

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

Эти сомнительные примеры будут сравниваться с лучшими подходами или альтернативами по мере продвижения по статье.

Это руководство предназначено в основном для опытных программистов на Python, но оно доступно для всех любознательных людей, интересующихся программированием и лямбда-исчислением.

Все примеры, включенные в это руководство, были протестированы с Python 3.7.

Лямбда-исчисление

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

История

Алонзо Черч формализовал лямбда-исчисление, язык, основанный на чистой абстракции, в 1930-х годах. Лямбда-функции также называют лямбда-абстракциями, что является прямой ссылкой на модель абстракции оригинального творения Алонзо Черча.

Лямбда-исчисление может кодировать любые вычисления. Это полный Тьюринг, но вопреки концепции машины Тьюринга, она чиста и не сохраняет никакого состояния.

Функциональные языки берут свое начало в математической логике и лямбда-исчислении, в то время как императивные языки программирования охватывают основанную на состоянии модель вычислений, изобретенную Аланом Тьюрингом. Две модели вычислений, лямбда-исчисление и машины Тьюринга, могут быть переведены друг в друга. Эта эквивалентность известна как гипотеза Черча-Тьюринга.

Функциональные языки напрямую наследуют философию лямбда-исчисления, применяя декларативный подход к программированию, который подчеркивает абстракцию, преобразование данных, композицию и чистоту (отсутствие состояния и побочных эффектов). Примеры функциональных языков включают Haskell, Lisp или Erlang.

Напротив, машина Тьюринга привела к императивному программированию, которое можно найти в таких языках, как Fortran, C или Python.

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

Разделение в обоих семействах имеет некоторые нюансы, поскольку некоторые функциональные языки включают императивные функции, такие как OCaml, в то время как функциональные функции пронизывают императивное семейство языков, в частности, с введением лямбда-функций в Java или Python..

Python по своей сути не является функциональным языком, но он изначально принял некоторые функциональные концепции. В январе 1994 года в язык были добавлены map(), filter(), reduce() и лямбда-оператор.

Первый пример

Вот несколько примеров, которые покажут вам аппетит к коду Python и функциональному стилю.

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

>>> def identity(x):
...     return x

identity() принимает аргумент x и возвращает его при вызове.

Напротив, если вы используете лямбда-конструкцию Python, вы получите следующее:

>>> lambda x: x

В приведенном выше примере выражение состоит из:

  • Ключевого слова: lambda
  • Связанной переменной: x
  • Тела: x

Примечание. В контексте этого урока связанная переменная является аргументом лямбда-функции.

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

Вы можете написать немного более сложный пример, функцию, которая добавляет 1 к аргументу, следующим образом:

>>> lambda x: x + 1

Вы можете применить указанную выше функцию к аргументу, заключив функцию и ее аргумент в круглые скобки:

>>> (lambda x: x + 1)(2)
3

Редукция — это стратегия лямбда-исчисления для вычисления значения выражения. В текущем примере он состоит из замены связанной переменной x аргументом 2:

(lambda x: x + 1)(2) = lambda 2: 2 + 1
                     = 2 + 1
                     = 3

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

>>> add_one = lambda x: x + 1
>>> add_one(2)
3

Вышеупомянутая лямбда-функция эквивалентна написанию этого:

def add_one(x):
    return x + 1

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

>>> full_name = lambda first, last: f'Full name: {first.title()} {last.title()}'
>>> full_name('guido', 'van rossum')
'Full name: Guido Van Rossum'

Лямбда-функция, назначенная для full_name, принимает два аргумента и возвращает строку, интерполирующую два параметра первым и последним. Как и ожидалось, определение лямбда перечисляет аргументы без круглых скобок, тогда как вызов функции выполняется точно так же, как обычная функция Python, с круглыми скобками вокруг аргументов.

Анонимные функции

Следующие термины могут использоваться взаимозаменяемо в зависимости от типа языка программирования и культуры:

  • Анонимные функции;
  • Лямбда-функции;
  • Лямбда-выражения;
  • Лямбда-абстракции;
  • Лямбда-форма;
  • Функциональные литералы.

В остальной части урока и после этого раздела вы в основном будете встречаться с термином «лямбда-функция».

Буквально, анонимная функция — это функция без имени. В Python анонимная функция создается с помощью ключевого слова лямбда. Говоря более свободно, ему может быть присвоено или нет имя. Рассмотрим анонимную функцию с двумя аргументами, определенную с помощью лямбда, но не связанную с переменной. Лямбда не имеет имени:

>>> lambda x, y: x + y

Приведенная выше функция определяет лямбда-выражение, которое принимает два аргумента и возвращает их сумму.

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

>>> _(1, 2)
3

В приведенном выше примере используется функция интерактивного интерпретатора, предоставляемая через подчеркивание (_). См. Примечание ниже для получения более подробной информации.

Вы не можете написать подобный код в модуле Python. Считайте _ в интерпретаторе побочным эффектом, которым вы воспользовались. В модуле Python вы должны присвоить имя лямбде или вы передадите лямбду функции. Эти два подхода вы будете использовать позже в этом уроке.

Примечание. В интерактивном интерпретаторе одиночное подчеркивание (_) привязано к последнему вычисленному выражению.

В приведенном выше примере _ указывает на лямбда-функцию. Дополнительные сведения об использовании этого специального символа в Python см. В статье Значение подчеркивания в Python.

Другой шаблон, используемый в других языках, таких как JavaScript — немедленное выполнение лямбда-функции Python. Это называется выражением немедленно вызываемой функции (IIFE, произносится как «iffy»). Вот пример:

>>> (lambda x, y: x + y)(2, 3)
5

Приведенная выше лямбда-функция определяется и затем сразу вызывается с двумя аргументами (2 и 3). Он возвращает значение 5, которое является суммой аргументов.

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

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

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

>>> high_ord_func = lambda x, func: x + func(x)
>>> high_ord_func(2, lambda x: x * x)
6
>>> high_ord_func(2, lambda x: x + 3)
7

Python предоставляет функции высшего порядка как встроенные функции или в стандартной библиотеке. Примеры включают map(), filter(), functools.reduce(), а также ключевые функции, такие как sort(), sorted(), min() и max(). Вы будете использовать лямбда-функции вместе с функциями высшего порядка Python в разделе «Соответствующее использование лямбда-выражений».

Лямбда-функции Python и регулярные функции

Эта цитата из FAQ по дизайну и истории Python, кажется, задает тон об общих ожиданиях в отношении использования лямбда-функций в Python:

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

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

Функции

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

Модуль dis предоставляет функции для анализа байт-кода Python, сгенерированного компилятором Python:

>>> import dis
>>> add = lambda x, y: x + y
>>> type(add)

>>> dis.dis(add)
  1           0 LOAD_FAST                0 (x)
              2 LOAD_FAST                1 (y)
              4 BINARY_ADD
              6 RETURN_VALUE
>>> add
 at 0x7f30c6ce9ea0>

Вы можете видеть, что dis() предоставляет доступную для чтения версию байт-кода Python, позволяющую проверять низкоуровневые инструкции, которые интерпретатор Python будет использовать при выполнении программы.

Теперь посмотрим на это с помощью обычного функционального объекта:

>>> import dis
>>> def add(x, y): return x + y
>>> type(add)

>>> dis.dis(add)
  1           0 LOAD_FAST                0 (x)
              2 LOAD_FAST                1 (y)
              4 BINARY_ADD
              6 RETURN_VALUE
>>> add

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

Отслеживание

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

>>> div_zero = lambda x: x / 0
>>> div_zero(2)
Traceback (most recent call last):
    File "", line 1, in 
    File "", line 1, in 
ZeroDivisionError: division by zero

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

Вот такое же исключение, вызываемое нормальной функцией:

>>> def div_zero(x): return x / 0
>>> div_zero(2)
Traceback (most recent call last):
    File "", line 1, in 
    File "", line 1, in div_zero
ZeroDivisionError: division by zero

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

Синтаксис

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

  • Он может содержать только выражения и не может включать в себя операторы.
  • Он записывается как одна строка исполнения.
  • Он не поддерживает аннотации типов.
  • Его можно вызвать немедленно (IIFE).

Нет операторов

Лямбда-функция не может содержать операторов. В лямбда-функции такие операторы, как return, pass, assert или raise, вызовут исключение SyntaxError. Вот пример добавления assert в тело лямбды:

>>> (lambda x: assert x == 2)(2)
  File "", line 1
    (lambda x: assert x == 2)(2)
                    ^
SyntaxError: invalid syntax

Этот надуманный пример предназначен для утверждения, что параметр x имеет значение 2. Но интерпретатор идентифицирует SyntaxError при анализе кода, который включает утверждение assert в теле лямбда-выражения.

Одно выражение

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

>>> (lambda x:
... (x % 2 and 'odd' or 'even'))(3)
'odd'

В приведенном выше примере возвращается строка «odd», если лямбда-аргумент нечетный, и «even», если аргумент четный. Он занимает две строки, поскольку заключен в круглые скобки, но остается одним выражением.

Аннотации типов

Если вы начали использовать подсказки типов, которые теперь доступны в Python, тогда у вас есть еще одна веская причина предпочесть обычные функции лямбда-функциям Python. Ознакомьтесь с Python Type Checking (Guide), чтобы узнать больше о подсказках типов Python и проверке типов. В лямбда-функции нет эквивалента для следующего:

def full_name(first: str, last: str) -> str:
    return f'{first.title()} {last.title()}'

Любая ошибка типа с full_name() может быть обнаружена такими инструментами, как mypy или pyre, тогда как SyntaxError с эквивалентной лямбда-функцией возникает во время выполнения:

>>> lambda first: str, last: str: first.title() + " " + last.title() -> str
  File "", line 1
    lambda first: str, last: str: first.title() + " " + last.title() -> str

SyntaxError: invalid syntax

Как и при попытке включить оператор в лямбда-выражение, добавление аннотации типа немедленно приводит к ошибке SyntaxError во время выполнения.

IIFE

Вы уже видели несколько примеров немедленного выполнения функций:

>>> (lambda x: x * x)(3)
9

Вне интерпретатора Python эта функция, вероятно, не используется на практике. Это прямое следствие того, что лямбда-функция вызывается в том виде, в котором она определена. Например, это позволяет вам передать определение лямбда-выражения Python функции более высокого порядка, такой как map(), filter() или functools.reduce(), или ключевой функции.

Аргументы

Декораторы

закрытие

Время оценки

Тестирование лямбда-выражений

Злоупотребления лямбда-выражениями

Создание исключения

Загадочный стиль

классы Python

Надлежащее использование лямбда-выражений

Классические функциональные конструкции

Основные функции

Интерфейсы пользовательского интерфейса

интерпретатор Python

timeit

Патчинг обезьян

Альтернативы лямбдам

Карта

Фильтр

Уменьшить

Являются ли лямбды питоническими или нет?

Заключение


По мотивам How to Use Python Lambda Functions

Опубликовано Вадим В. Костерин

ст. преп. кафедры ЦЭиИТ. Автор более 130 научных и учебно-методических работ. Лауреат ВДНХ (серебряная медаль).

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *