Python анонимные функции: Анонимная функция (лямбда-выражение) — Документация Python для сетевых инженеров 3.0

Содержание

Анонимные функции | Python: Функции

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

>>> lambda x: x + 1
<function <lambda> at 0x7f56e5798a60>

Мы сконструировали функцию, но имя она не получила, поэтому REPL её отобразил, как function <lambda>.

Ключевое слово lambda названо в честь лямбда абстракции — основного кирпичика Лямбда Исчисления, математического аппарата, часто применяющегося в разработке языков программирования. В Лямбда Исчислении все функции — анонимные, поэтому анонимные функции во многих языках тоже иногда называют «лямбдами» или «лямбда-функциями».

Рассмотрим пример, использующий анонимную функцию:

>>> l = [1, 2, 5, 3, 4]
>>> l.sort(key=lambda x: -x)
>>> l
[5, 4, 3, 2, 1]

Метод sort принимает в качестве аргумента key ссылку на функцию. В примере в качестве аргумента указана функция, меняющая знак у аргумента — поэтому список получается отсортирован от большего к меньшему. Сортировка с указанием ключа сама по себе встречается довольно часто, а вот ключи сортировки чаще всего будут разными. Поэтому выносить ключи в именованные функции смысла нет и анонимные функции здесь подходят идеально!

Особенности анонимных функций

Вы могли заметить, что аргументы анонимных функций не заключены в скобки. К этому нужно будет привыкнуть. Остальные средства для описания аргументов доступны в полной мере — и именованные аргументы, и *args с **kwargs.

А ещё вы могли обратить внимание на то, что в примерах функции явно что-то возвращают, но слово return нигде не указано! Дело в том, что тело лямбда-функции — это всегда одно выражение, результат вычисления которого и будет возвращаемым значением. Да, в теле лямбда-функции не получится выполнить несколько действий и не получится использовать многострочные конструкции вроде for и while. Но зато анонимные функции обычно просто читать, чего было бы сложно добиться, разреши авторы «многострочные» лямбды.

Ещё одна особенность лямбда-функций заключается в том, что само объявление функции является выражением! Функции можно конструировать и тут же вызывать, не заканчивая выражение:

>>> 1 + (lambda x: x * 5)(8) + 1
42

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

>>> def caller(arg):
...     return lambda f: f(arg)
...
>>> call_with_five = caller(5)
>>> call_with_five(str)
'5'
>>> call_with_five(lambda x: x + 1)
6

Python анонимные функции — Русские Блоги

В Python мы используем операторы def для определения функций, таких как:

def double(x):
    return 2 * x

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

lambda параметры:  выражение

Ключевое слово lambda указывает на то, что это анонимная функция, двоеточие: перед переменной стоит параметр анонимной функции, а после возвращаемого значения функции следует двоеточие. Обратите внимание, что ключевое слово return здесь не требуется.

Мы переписываем вышеприведенную двойную функцию как анонимную функцию следующим образом:

lambda x: 2 * x

Как вы называете анонимные функции? Может использоваться непосредственно так:

>>> (lambda x: 2 * x)(8)
16

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

>>> f = lambda x: 2 * x   
>>> f
<function <lambda> at 0x7f835a696578>
>>> f(8)
16
Используйте сцену

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

def func(g, arr):
    return [g(x) for x in arr]
    
def add_one(x):
    return x + 1
   
arr = func(add_one, [1, 2, 3, 4])

Вместо этого мы используем лямбду:

def func(g, arr):
    return [g(x) for x in arr]

arr = func(lambda x: x + 1, [1, 2, 3, 4])
резюме
  • Анонимные функции — это, по сути, функции без имен функций, поэтому вам не нужно беспокоиться о конфликте имен функций при использовании анонимных функций;
  • Анонимные функции обычно подходят для создания некоторых небольших временных функций;

синтаксис, аргументы и много примеров ~ PythonRu

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

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

Что такое лямбда в Python?

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

Для этого потребуется немного поменять направление мышление. Как вы знаете, все в Python является объектом.

Например, когда мы запускаем эту простейшую строку кода

x = 5

Создается объект Python типа int, который сохраняет значение 5. x же является символом, который ссылается на объект.

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

>>> type(x)
<class 'int'>
>>> id(x)
4308964832

В итоге x ссылается на объект типа int, а расположен он по адресу, который вернула функция id.

Просто и понятно.

А что происходит при определении вот такой функции:

>>> def f(x):
...     return x * x
...

Повторим упражнение и узнаем type и id объекта f.

>>> def f(x):
...     return x * x
...
>>> type(f)
<class 'function'>
>>> id(f)
4316798080

Оказывается, в Python есть класс function, а только что определенная функция f — это его экземпляр. Так же как x был экземпляром класса integer. Другими словами, о функциях можно думать как о переменных. Разница лишь в том, что переменные хранят данные, а функции — код.

Это же значит, что функции можно передать в качестве аргументов другим функциям или даже использовать их как тип возвращаемого значения.

Рассмотрим простой пример, где функция f передается другой функции.

def f(x):
    return x * x

def modify_list(L, fn):
    for idx, v in enumerate(L):
        L[idx] = fn(v)

L = [1, 3, 2]
modify_list(L, f)
print(L)



Попробуйте разобраться самостоятельно с тем, что делает этот код, прежде чем читать дальше.

Итак, modify_list — это функция, которая принимает список L и функцию fn в качестве аргументов. Затем она перебирает список элемент за элементом и применяет функцию к каждому из них.

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

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

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

lambda arguments: expression

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

>>> f = lambda x: x * x
>>> type(f)
<class 'function'>

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

Лямбда с несколькими аргументами

Определить лямбда-функцию с одним аргументом не составляет труда.

>>> f = lambda x: x * x
>>> f(5)
25

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

>>> f = lambda x, y: x * y
>>> f(5, 2)
10

Отлично! А как насчет лямбда-функции без аргументов?

Лямбда-функция без аргументов

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

>>> f = lambda: True
>>> f()
True

Несколько лямбда-функций

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

Ответ однозначен: нет.

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

Примеры лямбда-функций

Теперь рассмотрим самые распространенные примеры использования лямбда-функций.

Лямбда-функция и map

Распространенная операция со списками в Python — применение операции к каждому элементу.

map() — это встроенная функция Python, принимающая в качестве аргумента функцию и последовательность. Она работает так, что применяет переданную функцию к каждому элементу.

Предположим, есть список целых чисел, которые нужно возвести в квадрат с помощью map.

>>> L = [1, 2, 3, 4]
>>> list(map(lambda x: x**2, L))
[1, 4, 9, 16]

Обратите внимание на то, что в Python3 функция map возвращает объект Map, а в Python2 — список.

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

Вот еще один пример.

Лямбда-функция и filter

filter() — это еще одна встроенная функция, которая фильтрует последовательность итерируемого объекта.

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

Если возвращаемое значение — True, элемент остается. В противном случае — отклоняется. Определим, например, простую функцию, которая возвращает True для четных чисел и False — для нечетных:

def even_fn(x):
  if x % 2 == 0:
    return True
  return False

print(list(filter(even_fn, [1, 3, 2, 5, 20, 21])))



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

print(list(filter(lambda x: x % 2 == 0, [1, 3, 2, 5, 20, 21])))

И в этом сила лямбда-функций.

Лямбда-функция и сортировка списков

Сортировка списка — базовая операция в Python. Если речь идет о списке чисел или строк, то процесс максимально простой. Подойдут встроенные функции sort и sorted.

Но иногда имеется список кастомных объектов, сортировать которые нужно на основе значений одного из полей. В таком случае можно передать параметр key в sort или sorted. Он и будет являться функцией.

Функция применяется ко всем элементам объекта, а возвращаемое значение — то, на основе чего выполнится сортировка. Рассмотрим пример. Есть класс Employee.

class Employee:
    def __init__(self, name, age):
        self.name = name
        self.age = age

Теперь создадим экземпляры этого класса и добавим их в список.

Alex = Employee('Alex', 20)
Amanda = Employee('Amanda', 30)
David = Employee('David', 15)
L = [Alex, Amanda, David]

Предположим, что мы хотим отсортировать его на основе поля age сотрудников. Вот что нужно сделать для этого:

L.sort(key=lambda x: x.age)
print([item.name for item in L])


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

Пара слов о выражениях и инструкциях

Как уже упоминалось, лямбда могут иметь только одно выражение (expression) в теле.

Обратите внимание, что речь идет не об инструкции (statement).

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

Например, инструкция if или циклы for и while являются примерами инструкций. Заменить инструкцию на значение попросту невозможно.

А вот выражения — это значения. Запросто можно заменить все выражения в программе на значения, и программа продолжит работать корректно.

Например:

  • 3 + 5 — выражение со значением 8
  • 10 > 5 — выражение со значением True
  • True and (5 < 3) — выражение со значением False

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

Анонимные функции, ссылающиеся на локальные переменные в python

Closures в python с использованием functools.partial

from functools import partial

i = 0
f0 = partial(callback, i)
i = 1
f1 = partial(callback, i)

f0()
# 0
f1()
# 1

partial похож на lambda, но обертывает значение в этот момент в arg. Не оценивая его, когда его называют.

Обертывание только некоторых из аргументов

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

def callback(val1, val2):
   print "{0} {1}".format(val1, val2)

i = 0
x = 8
f0 = partial(callback, i)
f0(x)
# 0 8

По сути, вы завернули callback(val1, val2) в callback(val2) , а val1 уже включен в качестве закрытия.

Пример аналогичного эффекта с использованием lambda

В случае, если вы действительно хотите увидеть, как это сделать с закрытием lambda, вы можете понять, почему это становится уродливым и предпочтительным частичным…

f0 = (lambda val1: lambda val2: callback(val1, val2))(i)

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

Трассировки из исключений: частичные vs lambda vs вложенные функции

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

Рассмотрим эту версию, которая поднимет деление на ноль:

def callback(val1, val2):
    return val1 / val2

Нормальное наружное/внутреннее закрытие

def wrapper(fn, val1):
    def wrapped(val2):
            return fn(val1, val2)
    return wrapped

f0 = wrapper(callback, i)
f0(0)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in wrapped
  File "<stdin>", line 2, in callback
ZeroDivisionError: integer division or modulo by zero

lambda закрытие

f0 = (lambda val1: lambda val2: callback(val1, val2))(i)
f0(0)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
  File "<stdin>", line 2, in callback
ZeroDivisionError: integer division or modulo by zero

А теперь для functools.partial

f0 = partial(callback, i)
f0(0)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in callback
ZeroDivisionError: integer division or modulo by zero

4 Продвинутых приема работы с функциями Python, о которых вы могли не знать | by Андрей Шагин | NOP::Nuances of Programming

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

У именованных аргументов есть несколько преимуществ:

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

Возможно, все это вам уже знакомо. Но знаете ли вы о возможности принудительно задавать именованные аргументы? Подробно это описано в PEP 3202. Если вкратце, то все сводится к использованию звездочки перед каждым аргументом, который будет принудительно задан в качестве именованного аргумента. Или сразу перед всеми, и тогда все аргументы будут заданы как именованные аргументы:

>>> def f(*, a, b):
... print(a, b)
...
>>> f(1, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() takes 0 positional
arguments but 2 were given
>>> f(a=1, b=2)
1 2
>>>

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

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

>>> def f(a, b):
... print(a, b)
...
>>> args = { "a": 1, "b": 2 }
>>> f(**args)
1 2

Аналогично используется одинарная звездочка * для распаковывания массива и передачи его содержимого в функцию в качестве позиционных аргументов:

>>> def f(a, b, c):
... print(a, b, c)
...
>>> l = [1, 2, 3]
>>> f(*l)
1 2 3

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

Создадим наш собственный декоратор. Это проще, чем кажется, и когда-нибудь он пригодится:

def print_argument(func):
def wrapper(the_number):
print("Argument for", func.__name__, "is", the_number)
return func(the_number)

return wrapper

@print_argument
def add_one(x):
return x + 1

print(add_one(1))

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

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

В качестве результата нашего маленького скрипта будет выводиться:

Argument for add_one is 1
2

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

Лямбда-функция назначается переменной, и таким образом получается лаконичный способ определения функции:

>>> def add_one(x): return 2*x
>>> add_one(3)
4

Еще интереснее становится, когда нужно использовать функцию в качестве аргумента. В таких случаях функция обычно используется только один раз. Вы наверняка знаете, что с помощью map функция применяется ко всем элементам итерируемого объекта. Используем лямбду при вызове map:

>>> numbers = [1, 2, 3, 4]
>>> times_two = map(lambda x: x * 2, numbers)
>>> list(times_two)
[2, 4, 6, 8]
>>>

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

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

Читайте нас в Telegram, VK и Яндекс.Дзен

Функции в Python. Часть 2.

 

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

Анонимные функции или лямбда-выражения – не являются ноу-хау Python, такие конструкции характерны для функциональных языков, таких как Haskell или Lisp. Появились они, благодаря деятельности двух математиков Алонзо Черча и Алана Тьюринга.

Python не является функциональным языком программирования, но некоторые элементы функциональных языков встроены в него.

Давайте разберемся, что же такое лямбда-выражения и как с ними работать. Как можно заметить из названия “анонимная функция”, лямбда-выражение в буквальном смысле является функцией, у которой отсутствует имя:

def simple_sqr(x):
    return x*x


lambda_sqr = lambda x: x*x

print(lambda_sqr(5)) #25
print(simple_sqr(6)) #36

В данном примере мы написали обычную именованную, def-функцию и лямбда-выражение, которые возводит число в квадрат. Вы можете справедливо возразить, сказав, что не видите здесь ни одной анонимной функции, есть simple_sqr и lambda_sqr, к которой присваивается странная конструкция. Этот пример является несколько надуманным, если записать это выражение в PyCharm, то услужливая среда разработки заметит: “PEP 8: do not assign a lambda expression, use a def”, что в прямом смысле означает, что не надо мудрить с лямбда-выражениями, используйте обычную именованную def-функцию. Однако на данном примере легко проследить логику анонимных функций их синтаксис:

Ключевое слово: lambda

Аргумент (то, что мы передаем в функцию): x

Тело функции (действие над аргументом): x*x

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

print((lambda x: 'even' if x % 2 == 0 else 'odd')(5)) #odd
print((lambda x: 'even' if x % 2 == 0 else 'odd')(4)) #even

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

Функция map()

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

str_list = ['1', '2', '3', '4', '5'] #рассмотрим список с элементами типа string

int_list = list(map(int, str_list)) #преобразуем их в int

print(int_list) #[1, 2, 3, 4, 5]

sqr_list = list(map(lambda x: x*x, int_list)) #возведем их в квадрат

print(sqr_list) #[1, 4, 9, 16, 25]

print(list(map(lambda x: x*x, list(map(int, str_list)))))#вышеизложенный код в одну строку

Также замечу, что в map можем передавать несколько элементов, которые могут быть разной длины, но map будет выполнять действие только по минимальному элементу.

Функция reduce()

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

from functools import reduce
print(sqr_list) #[1, 4, 9, 16, 25, 36]
print(reduce(lambda x, y: x + y, sqr_list)) #91 

Синтаксис reduce похож на map, нам необходимо передать значения и некий функционал для их обработки. Замечу, что функции reduce не является стандартной и для ее использования нам необходимо подключить библиотеку functools.

Функция filter()

Как видно из названия, filter(function, iterator) – фильтрует элементы по заданным условиям, синтаксис filter также совпадает с map, нам необходимо передать туда условия, по которым будут отбираться значения и сами значения. В нашем примере, мы ищем в строке слова с количеством символов больше 5:

str = 'We learn python with ai-Simple'
new_list = str.split() #['We', 'learn', 'python', 'with', 'ai-Simple']
print(list(filter(lambda x: len(x) > 5, new_list))) #['python', 'ai-Simple']

Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.

Функциональное программирование на Python для самых маленьких — Часть 1 — Lambda Функция

Я решил написать эту серию статей, ибо считаю, что никто не должен сталкиваться с той стеной непонимания, с которой столкнулся когда-то я.

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

Небольшое введение

Для начала, давайте разберемся, что такое функциональное программирование, в чем его особенности, зачем оно было придумано, а также где и как его использовать. Стоп… А зачем? Об этом написаны тонны материалов, да и в этой статье судя по всему эта информация не особо нужна. Эта статья написана для того, чтобы читатели научились разбираться в коде, который написан в функциональном стиле. Но если вы все-таки хотите разобраться в истории Функционального Программирования и в том, как оно работает под капотом, то советую вам почитать о таких вещах, как

  • Чистая Функция
  • Функции высшего порядка

Давайте кратко разберемся в этих двух понятиях прежде чем продолжим.

Чистая Функция — Функция которая является детерминированной и не обладает никакими побочными эффектами.

То есть чтобы функция являлась чистой она должна быть детерминированной — то есть каждый раз при одинаковом наборе аргументов выдавать одинаковый результат.

Пример детерминированной функции

def sum(a,b):
  return a+b

print(sum(3,6))
print(sum(3,6))
print(sum(3,6))

>>>9
>>>9
>>>9

И пример не детерминированной:

from datetime import date

todays_weekday = date.today().weekday()
def sum(a,b):
  if todays_weekday == 1:
    result = a+b
    print(f'sum is {result} and result class is {type(result)}')
  else:
    result = str(a+b)
    print(f'sum is {result} and result class is {type(result)}')

sum(4,5)
todays_weekday = 4
sum(4,5)

>>>sum is 9 and result class is <class 'int'>
>>>sum is 9 and result class is <class 'str'>

Каждый раз при смене дня недели (который не является аргументом функции) функция выдает разные результаты.

Самый очевидный пример не детерминированной функции это random:

import random
print(random.random())
print(random.random())
print(random.random())

>>>0.5002395668892657
>>>0.8837128676460416
>>>0.5308851462814731

Второе важное качество чистой функции это отсутствие побочных эффектов.

Покажем наглядно:

my_list = [32,3,50,2,29,43]

def sort_by_sort(li):
  li.sort()
  print(li)

sort_by_sort(my_list)
print(my_list)

>>>[2, 3, 29, 32, 43, 50]
>>>[2, 3, 29, 32, 43, 50]

Функция sort_by_sort имеет побочные эффекты потому что изменяет исходный список элементов и выводит что то в консоль.

my_list = [32,3,50,2,29,43]

def sort_by_sorted(li):
  return sorted(li)

print(sort_by_sorted(my_list))
print(my_list)

>>>[2, 3, 29, 32, 43, 50]
>>>[32, 3, 50, 2, 29, 43]

В отличии от предыдущего примера функция sort_by_sorted не меняет исходного массива и возвращает результат не выводя его в консоль самостоятельно.

Чистые функции хороши тем что:

  • Они проще читаются
  • Они проще поддерживаются
  • Они проще тестируются
  • Они не зависят от того в каком порядке их вызывать

Функциональное программирование можно охарактеризовать тем что в нем используются только чистые функции.

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

Пример ФВП:

def func(num):
  return num**2

def higher_order_func(fun, num):
  return fun(num)+fun(num)

print(func(4))
print(higher_order_func(func,4))

>>>16
>>>32

С основами чуть чуть разобрались и теперь перейдем к следующему шагу.

Итак, начнем

Для начала надо понять следующее — что такое Функциональное Программирование вообще. Лично я знаю две самые часто упоминаемые парадигмы в повседневном программировании — это ООП и ФП.

Если упрощать совсем и объяснять на пальцах, то описать эти две парадигмы можно следующим образом:

  • ООП — это Объектно Ориентированное Программирование — подход к программированию, при использовании которого объекты можно передавать в качестве параметров и использовать их в качестве значений.
  • По такой логике можно установить, что ФП — подход к программированию, при использовании которого функции можно передавать другим функциям в качестве параметров и использовать функции в качестве значений, возвращаемых другими функциями… Ответ скрыт в самом названии.

Как говорил мой любимый учитель

zverok

Виктор Шепелев: «Вся работа в программировании — это работа с данными. Взял какие-то данные, поигрался с ними и вернул обратно.»

Это относится и к ФП — взял какие-то данные, взял какую-то функцию, поигрался с ними и выдал что-то на выходе.

Не стану расписывать всё, иначе это будет оооочень долго. Цель данной статьи — помочь разобраться, а не объяснить, как и что работает, поэтому тут мы рассмотрим основные функции из ФП.

В большинстве своем ФП (как я его воспринимаю) — это просто упрощенное написание кода. Любой код, написанный в функциональном стиле, может быть довольно легко переписан в обычном стиле без потери качества, но более примитивно. Цель ФП заключается в том, чтобы писать код более простой, понятный и который легче поддерживать, а также который занимает меньше памяти, ну и куда же без этого — разумеется, главная вечная мораль программирования — DRY (Don’t Repeat Yourself — Не повторяйся).

Сейчас мы с вами разберем одну из основных функций, которая применяется в ФП — Lambda функцию.

В следующих статьях мы разберем такие функции как Map, Zip, Filter и Reduce.

Lambda функция

Lambda — это инструмент в python и других языках программирования для вызова анонимных функций. Многим это скорее всего ничего не скажет и никак не прояснит того, как она работает, поэтому я расскажу вам просто механизм работы lambda выражений.

Все очень просто.

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

Формула площади круга это

S = pi*(r**2)

где

S — это площадь круга

pi — математическая константа равная 3.14 которую мы получим из стандартной библиотеки Math

r — радиус круга — единственная переменная которую мы будем передавать нашей функции

Теперь оформим это все в python:

import math #Подключаем библиотеку math

pi_const = round(math.pi, 2) #округляем pi до второго знака после запятой иначе она будет выглядеть как 3.141592653589793 а нам это будет неудобно

# Пишем функцию которая будет вычислять площадь круга по заданному радиусу в обычном варианте записи
def area_of_circle_simple(radius):
  return pi_const*(radius**2)

print(area_of_circle_simple(5))
print(area_of_circle_simple(12))
print(area_of_circle_simple(26))
>>>78.5
>>>452.16
>>>2122.64

Вроде бы неплохо, но это всё может выглядеть куда круче, если записывать это через lambda:

import math #Подключаем библиотеку math

pi_const = round(math.pi, 2) #округляем pi до второго знака 
# после запятой иначе она будет выглядеть 
# как 3.141592653589793 а нам это будет неудобно

print((lambda radius: pi_const*(radius**2))(5))
print((lambda radius: pi_const*(radius**2))(12))
print((lambda radius: pi_const*(radius**2))(26))

>>>78.5
>>>452.16
>>>2122.64

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

Лямбда функция работает по следующему принципу


print((lambda перечисляются аргументы через запятую : что то с ними делается)(передаем аргументы))

>>>получаем результат того что находится после двоеточия строкой выше

Рассмотрим пример с двумя входными аргументами. Например, нам надо посчитать объем конуса по следующей формуле:

V = (height*pi_const*(radius**2))/3

Запишем это все в python:

import math #Подключаем библиотеку math

pi_const = round(math.pi, 2) #округляем pi до второго знака после запятой иначе она будет выглядеть как 3.141592653589793 а нам это будет неудобно

#Формула объема конуса в классической форме записи
def cone_volume(height, radius):
  volume = (height*pi_const*(radius**2))/3
  return volume

print(cone_volume(3, 10))

>>>314.0

А теперь как это будет выглядеть в lambda форме:

import math #Подключаем библиотеку math

pi_const = round(math.pi, 2) #округляем pi до второго знака после запятой иначе она будет выглядеть как 3.141592653589793 а нам это будет неудобно

print((lambda height, radius : (height*pi_const*(radius**2))/3)(3, 10))

>>>314.0

Количество переменных здесь никак не ограничено. Для примера посчитаем объем усеченного конуса, где у нас учитываются 3 разные переменные.

Объем усеченного конуса считается по формуле:

V = (pi_const*height*(r1**2 + r1*r2 + r2**2))/3

И вот, как это будет выглядеть в python классически:

import math #Подключаем библиотеку math

pi_const = round(math.pi, 2) #округляем pi до второго знака после запятой иначе она будет выглядеть как 3.141592653589793 а нам это будет неудобно

#Формула объема усеченного конуса в классической записи
def cone_volume(h,r1,r2):
  return (pi_const * h * (r1 ** 2 + r1 * r2 + r2 ** 2))/3

print(cone_volume(12, 8, 5))
print(cone_volume(15, 10, 6))
print(cone_volume(20, 12, 9))

>>>1620.24
>>>3077.20
>>>6970.8

А теперь покажем, как это будет выглядеть с lambda:

import math #Подключаем библиотеку math

pi_const = round(math.pi, 2) #округляем pi до второго знака после запятой иначе она будет выглядеть как 3.141592653589793 а нам это будет неудобно

print((lambda height, radius1, radius2 : (height*pi_const*(radius1**2 + radius1*radius2 + radius2**2))/3)(12, 8, 5))
print((lambda height, radius1, radius2 : (height*pi_const*(radius1**2 + radius1*radius2 + radius2**2))/3)(15, 10, 6))
print((lambda height, radius1, radius2 : (height*pi_const*(radius1**2 + radius1*radius2 + radius2**2))/3)(20, 12, 9))

>>>1620.24
>>>3077.20
>>>6970.8

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

Сортировать одномерные списки в python с помощью lambda довольно глупо — это будет выглядеть, как бряцание мускулами там, где оно совсем не нужно.

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

new_int_list = [43,23,56,75,12,32] # Создаем список чисел
print(sorted(new_int_list)) # Сортируем список чисел
new_string_list = ['zum6z', 'yybt0', 'h2uwq', '2k9f9', 'hin9h', 'b0p0m'] # Создаем список строк
print(sorted(new_string_list)) # Сортируем список строк

>>>[12, 23, 32, 43, 56, 75]
>>>['2k9f9', 'b0p0m', 'h2uwq', 'hin9h', 'yybt0', 'zum6z']

В таких ситуациях, действительно, хватает обычного sorted() (ну или sort(), если вам нужно изменить текущий список на месте без создания нового, изменив исходный).

Но что, если нужно отсортировать список словарей по разным ключам? Тут может быть запись как в классическом стиле, так и в функциональном. Допустим, у нас есть список книг вселенной Песни Льда и Пламени с датами их публикаций и количеством страниц в них.

Как всегда, начнем с классической записи.

# Создали список из словарей книг
asoiaf_books = [
  {'title' : 'Game of Thrones', 'published' : '1996-08-01', 'pages': 694},
  {'title' : 'Clash of Kings', 'published' : '1998-11-16', 'pages': 761},
  {'title' : 'Storm of Swords', 'published' : '2000-08-08', 'pages': 973},
  {'title' : 'Feast for Crows', 'published' : '2005-10-17', 'pages': 753},
  {'title' : 'Dance with Dragons', 'published' : '2011-07-12', 'pages': 1016}
]

# Функция по получению названия книги
def get_title(book):
    return book.get('title')

# Функция по получению даты публикации книги
def get_publish_date(book):
    return book.get('published')

# Функция по получению количества страниц в книге
def get_pages(book):
    return book.get('pages')

# Сортируем по названию
asoiaf_books.sort(key=get_title)
for book in asoiaf_books:
  print(book)
print('-------------')
# Сортируем по датам
asoiaf_books.sort(key=get_publish_date)
for book in asoiaf_books:
  print(book)
print('-------------')
# Сортируем по количеству страниц
asoiaf_books.sort(key=get_pages)
for book in asoiaf_books:
  print(book)

>>>{'title': 'Clash of Kings', 'published': '1998-11-16', 'pages': 761}
>>>{'title': 'Dance with Dragons', 'published': '2011-07-12', 'pages': 1016}
>>>{'title': 'Feast for Crows', 'published': '2005-10-17', 'pages': 753}
>>>{'title': 'Game of Thrones', 'published': '1996-08-01', 'pages': 694}
>>>{'title': 'Storm of Swords', 'published': '2000-08-08', 'pages': 973}
>>>-------------
>>>{'title': 'Game of Thrones', 'published': '1996-08-01', 'pages': 694}
>>>{'title': 'Clash of Kings', 'published': '1998-11-16', 'pages': 761}
>>>{'title': 'Storm of Swords', 'published': '2000-08-08', 'pages': 973}
>>>{'title': 'Feast for Crows', 'published': '2005-10-17', 'pages': 753}
>>>{'title': 'Dance with Dragons', 'published': '2011-07-12', 'pages': 1016}
>>>-------------
>>>{'title': 'Game of Thrones', 'published': '1996-08-01', 'pages': 694}
>>>{'title': 'Feast for Crows', 'published': '2005-10-17', 'pages': 753}
>>>{'title': 'Clash of Kings', 'published': '1998-11-16', 'pages': 761}
>>>{'title': 'Storm of Swords', 'published': '2000-08-08', 'pages': 973}
>>>{'title': 'Dance with Dragons', 'published': '2011-07-12', 'pages': 1016}

А теперь перепишем это все через lambda функцию:

# Создали список из словарей книг
asoiaf_books = [
  {'title' : 'Game of Thrones', 'published' : '1996-08-01', 'pages': 694},
  {'title' : 'Clash of Kings', 'published' : '1998-11-16', 'pages': 761},
  {'title' : 'Storm of Swords', 'published' : '2000-08-08', 'pages': 973},
  {'title' : 'Feast for Crows', 'published' : '2005-10-17', 'pages': 753},
  {'title' : 'Dance with Dragons', 'published' : '2011-07-12', 'pages': 1016}
]

# Сортируем по названию
for book in sorted(asoiaf_books, key=lambda book: book.get('title')):
  print(book)

print('-------------')

# Сортируем по датам
for book in sorted(asoiaf_books, key=lambda book: book.get('published')):
  print(book)

print('-------------')

# Сортируем по количеству страниц
for book in sorted(asoiaf_books, key=lambda book: book.get('pages')):
  print(book)

>>>{'title': 'Clash of Kings', 'published': '1998-11-16', 'pages': 761}
>>>{'title': 'Dance with Dragons', 'published': '2011-07-12', 'pages': 1016}
>>>{'title': 'Feast for Crows', 'published': '2005-10-17', 'pages': 753}
>>>{'title': 'Game of Thrones', 'published': '1996-08-01', 'pages': 694}
>>>{'title': 'Storm of Swords', 'published': '2000-08-08', 'pages': 973}
>>>-------------
>>>{'title': 'Game of Thrones', 'published': '1996-08-01', 'pages': 694}
>>>{'title': 'Clash of Kings', 'published': '1998-11-16', 'pages': 761}
>>>{'title': 'Storm of Swords', 'published': '2000-08-08', 'pages': 973}
>>>{'title': 'Feast for Crows', 'published': '2005-10-17', 'pages': 753}
>>>{'title': 'Dance with Dragons', 'published': '2011-07-12', 'pages': 1016}
>>>-------------
>>>{'title': 'Game of Thrones', 'published': '1996-08-01', 'pages': 694}
>>>{'title': 'Feast for Crows', 'published': '2005-10-17', 'pages': 753}
>>>{'title': 'Clash of Kings', 'published': '1998-11-16', 'pages': 761}
>>>{'title': 'Storm of Swords', 'published': '2000-08-08', 'pages': 973}
>>>{'title': 'Dance with Dragons', 'published': '2011-07-12', 'pages': 1016}

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

Если вы повторите весь этот код самостоятельно, написав его сами, то я уверен, что с этого момента вы сможете сказать, что отныне вы понимаете, как работают lambda выражения, и сможете применять их в работе.

Но где же тут та самая экономия места, времени и памяти? Экономится максимум пара строк.

И вот тут мы подходим к реально интересным вещам.

Которые разберем в следующей статье, где мы обсудим map функцию.

UPD: По многочисленным просьбам, расставил знаки препинания.

Python Lambda (анонимная) Функция

Что такое лямбда-функции в Python?

В Python анонимная функция — это функция, которая определяется без имени.

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

Следовательно, анонимные функции также называются лямбда-функциями.


Как использовать лямбда-функции в Python?

Лямбда-функция в Python имеет следующий синтаксис.

Синтаксис лямбда-функции в Python

лямбда-аргументы: выражение 

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


Пример лямбда-функции в python

Вот пример лямбда-функции, которая удваивает входное значение.

  # Программа для демонстрации использования лямбда-функций
double = лямбда x: x * 2

печать (двойная (5))  

Выход

  10  

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

У этой функции нет названия. Он возвращает функциональный объект, которому присвоен идентификатор double . Теперь мы можем вызывать это как обычную функцию. Выписка

двойной = лямбда x: x * 2 

почти то же самое, что:

def double (x):
   возврат x * 2 

Использование лямбда-функции в Python

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

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

Пример использования с filter ()

Функция filter () в Python принимает в качестве аргументов функцию и список.

Функция вызывается со всеми элементами в списке, и возвращается новый список, содержащий элементы, для которых функция оценивает значение True .

Вот пример использования функции filter () для фильтрации только четных чисел из списка.

  # Программа для фильтрации только четных элементов из списка
my_list = [1, 5, 4, 6, 8, 11, 3, 12]

new_list = список (фильтр (лямбда x: (x% 2 == 0), my_list))

печать (новый_лист)  

Выход

  [4, 6, 8, 12]  

Пример использования с map ()

Функция map () в Python принимает функцию и список.

Функция вызывается со всеми элементами в списке, и возвращается новый список, содержащий элементы, возвращаемые этой функцией для каждого элемента.

Вот пример использования функции map () для удвоения всех элементов в списке.

  # Программа для удвоения каждого элемента в списке с помощью map ()

my_list = [1, 5, 4, 6, 8, 11, 3, 12]

new_list = список (карта (лямбда x: x * 2, my_list))

печать (новый_лист)  

Выход

  [2, 10, 8, 12, 16, 22, 6, 24]  

анонимных функций в Python.Введение в лямбда-выражения | Садрах Пьер, доктор философии

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

Приступим!

Предположим, у нас есть однострочная функция, которая возвращает разницу между двумя входными целочисленными значениями:

 def subtract (x, y): 
return x - y

Если мы вызываем эту функцию с x = 10 и y = 7 , и распечатав возвращаемое значение, мы получим:

 print ("Difference:", вычесть (10, 7)) 

Так как это простая функция, которая не делает ничего, кроме оценки одного выражения «xy», она может быть заменено лямбда-выражением:

 subtract_value = lambda x, y: x - y 

Лямбда-выражение содержит ключевое слово «lambda», входные данные x и y, двоеточие и выражение, которое мы хотим оценить.Если мы вернемся к нашему исходному определению функции:

 def subtract (x, y): 
return x - y

Мы увидим, что эти выражения выглядят очень похоже. Самая большая разница заключается в отсутствии ключевого слова def, return и имени функции в нашем лямбда-выражении.

Давайте напечатаем функцию с x = 10 и y = 7 в качестве входных данных:

 print ("Difference:", subtract_value (10,7)) 

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

 names = [ ' Guido van Rossum ',  'Bjarne Stroustrup', 'James Gosling', 'Rasmus Lerdorf'] 

Мы можем использовать ‘sorted () ‘вместе с лямбда-выражением для возврата списка в алфавитном порядке по фамилии:

 print (sorted (names, key = lambda name: name.split () [- 1] .lower ())) 

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

Еще мы можем подумать о том, как фиксировать переменные в анонимных функциях. Давайте определим целое число x = 5 и определим анонимную функцию, которая складывает x и другое входное целое число, y:

 x = 5 
add = lambda y: x + y

Давайте вызовем нашу функцию с y = 24:

 print ("Addition:", add (24)) 

Если мы изменим значение x в следующей строке и вызовем нашу функцию, мы получим:

 x = 5 
add = lambda y: x + y
print ( «Добавление:», добавить (24))
x = 10
print («Дополнение:», добавить (24))

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

 x = 5 
add = lambda y, x = x: x + y
print ( «Сложение:», добавить (24))
x = 10
print («Сложение:», добавить (24))

Мы получаем желаемое поведение. На этом я остановлюсь, но я призываю вас поиграть с кодом самостоятельно.

Подводя итог, в этом посте мы обсудили, как определять анонимные функции в Python с помощью лямбда-выражений.Если у вас есть сценарий Python, содержащий множество функций, которые оценивают однострочные выражения, часто проще заменить эти функции лямбда-выражениями. Далее мы обсудили, как установить значение по умолчанию в нашем лямбда-выражении, что позволило нам зафиксировать значение переменной в точке определения. Если вы хотите узнать больше об анонимных функциях, я рекомендую прочитать The Python Cookbook . Я надеюсь, что вы нашли этот пост интересным / полезным. Код в этом посте доступен на GitHub.Спасибо за чтение!

Лямбда-функции и анонимные функции в Python

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

 лямбда [аргументы]: выражение
 

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

  квадрат = лямбда x: x * x
  

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

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

  def квадрат (x):
    возврат x * x  

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

  >>> greet = lambda name: print ('Привет', имя)
>>> привет ('Стив')
Привет Стив
  

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

Следующая лямбда-функция содержит несколько параметров:

  >>> сумма = лямбда x, y, z: x + y + z
>>> сумма (5, 10, 15)
30
  

Следующая лямбда-функция может принимать любое количество параметров:

  >>> сумма = лямбда * x: x [0] + x [1] + x [2] + x [3]
>>> сумма (5, 10, 15, 20)
50
  

Лямбда-функция без параметров

Ниже приведен пример лямбда-функции без параметров.

  >>> greet = lambda: print ('Привет, мир!')
>>> привет ()
Привет, мир!
  

Анонимная функция

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

  >>> (лямбда x: x * x) (5)
25
  

Выше лямбда x: x * x определяет анонимную функцию и вызывает ее один раз, передав аргументы в скобках (лямбда x: x * x) (5) .

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

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

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

  >>> def dosomething (fn):
print ('Аргумент вызывающей функции:')
fn ()
>>> dosomething (lambda: print ('Hello World')) # передача анонимной функции
Аргумент вызывающей функции:
Привет, мир
>>> myfn = lambda: print ('Привет, мир')
>>> dosomething (myfn) # передача лямбда-функции
  

Выше функция dosomething () определяется параметром fn , который вызывается как функция внутри dosomething () . dosomething (lambda: print ('Hello World')) вызывает функцию dosomething () с анонимной лямбда-функцией в качестве аргумента.

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

  >>> sqrList = map (lambda x: x * x, [1, 2, 3, 4]) # передача анонимной функции
>>> следующий (sqrList)
1
>>> следующий (sqrList)
4
>>> следующий (sqrList)
9
>>> следующий (sqrList)
16
>>> следующий (sqrList)
25
  

лямбда-функций в Python: руководство для начинающих!

Эта статья была опубликована в рамках Data Science Blogathon

Введение

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

  • Что такое анонимные или лямбда-функции в Python?
  • Как использовать лямбда-функции в Python?
  • Пример лямбда-функций в Python
  • Использование лямбда-функций в Python
  • Зачем нужны лямбда-функции?
  • Когда не следует использовать лямбда-функции?
  • Лямбда-функции в map (), filter () и reduce ()

Источник изображения: Google Images

Что такое анонимные функции в Python?

В Python анонимная функция может быть функцией, определенной без репутации.

При определении обычных функций мы используем ключевое слово def в Python, но при определении анонимных функций мы используем ключевое слово lambda.

Следовательно, анонимные функции также называются Лямбда-функции .

Как использовать лямбда-функции в Python?

Лямбда-функция в Python имеет следующий синтаксис.

Синтаксис лямбда-функции в Python:

 лямбда-аргументы: выражение 

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

Пример лямбда-функции в Python

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

Программа для индикации использования лямбда-функций:

 double = лямбда x: x * 2
печать (двойная (10)) 

Выход:

 20 

Пояснение кода:

В приведенном выше коде лямбда x: x * 2 — это лямбда-функция.Кроме того, их x является аргументом, а x * 2 — выражением, которое оценивается и возвращается пользователю.

У этой функции нет названия. Он возвращает объект функции, который назначен идентификатору double. теперь мы назовем это стандартной функцией. Выписка

 двойной = лямбда x: x * 2 

почти идентичен:

 def double (x):
возврат x * 2 

Использование лямбда-функций в Python

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

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

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

Зачем нужны лямбда-функции?

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

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

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

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

Когда не следует использовать лямбда-функции?

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

Таким образом, лучше всего помнить, что простой код часто лучше, чем сложный.

Лямбда-функции в фильтре ()

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

Элементы, которые могут быть выбраны, основаны на некотором предопределенном ограничении. Принимает 2 параметра:

  • Функция, определяющая ограничение фильтрации.
  • Последовательность (любой итератор, например списки, кортежи и т. Д.)

Например,

 последовательность = [10,2,8,7,5,4,3,11,0, 1]
filter_answer = filter (лямбда x: x> 6, последовательности)
print (список (filter_answer)) 

Выход:

 [10, 8, 7, 11] 

Пояснение кода:

  • В первом операторе мы определяем контейнер списка, называемый последовательностями, которые содержат некоторые числа.
  • Здесь мы объявляем переменную с именем filter_answer, которая может хранить отфильтрованные значения, возвращаемые функцией filter ().
  • Лямбда-функция, которая запускается для каждого элемента списка и возвращает истину, если он больше 6.
  • Вывести результат, возвращенный функцией фильтра.

Лямбда-функции в карте ()

Функция карты используется для использования определенной операции для каждого элемента в последовательности. Как и filter (), он также принимает 2 параметра:

  • Функция, определяющая порядок выполнения операций над элементами.
  • Одна или несколько последовательностей.

Например,

В этом примере мы могли бы создать программу, которая печатает квадраты чисел в чрезвычайно заданном списке:

 последовательность = [10,2,8,7,5,4,11]
squared_result = map (лямбда x: x * x, последовательности)
печать (список (квадрат_результат)) 

Выход:

 [100, 4, 64, 49, 25, 16, 121] 

Пояснение кода:

  • Здесь мы определяем список, содержащий последовательности имен, которые содержат некоторые числа.
  • Мы объявляем переменную с именем squared_result, которая может хранить отображенные значения.
  • Лямбда-функция запускается для каждого элемента списка и возвращает квадрат этого числа.
  • Распечатайте результат, возвращенный функцией карты.

Лямбда-функции в reduce ()

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

Шаг-1: Выполнение определенной операции над двумя основными элементами последовательности.

Шаг 2: Сохранить результат

Шаг-3: Выполните операцию с сохраненным результатом и, следовательно, со следующим элементом в последовательности.

Шаг 4: Повторяйте, пока не останутся все элементы.

Также принимает два параметра:

  • Функция, определяющая порядок выполнения операций
  • Последовательность (любой итератор, например списки, кортежи и т. Д.)

Например,

В этом примере мы создадим программу, которая возвращает произведение всех элементов, представленных в списке, заданном пользователем.

 из functools import reduce
последовательности = [1,2,3,4,5,6]
product = reduce (лямбда x, y: x * y, последовательности)
печать (продукт) 

Выход:

 720 

Пояснение кода:

  • Импортируйте сокращение из модуля functools.
  • Здесь мы определяем контейнер списка с именем последовательностей, который содержит некоторые числа.
  • Мы объявляем переменную с именем product, в которой может храниться уменьшенное значение.
  • Лямбда-функция пошагово запускается для каждого элемента списка и возвращает произведение этого числа согласно предыдущему результату.
  • Вывести результат, возвращенный функцией уменьшения.

На этом наше обсуждение заканчивается!

Примечания к концу

Надеюсь, вам понравилась статья.

Если вы хотите связаться со мной, пожалуйста, свяжитесь со мной на Электронная почта

Ваши предложения и сомнения приветствуются здесь, в разделе комментариев. Спасибо, что прочитали мою статью!

Носители, показанные в этой статье, не принадлежат Analytics Vidhya и используются по усмотрению автора.

Python Лямбда


Лямбда-функция — это небольшая анонимная функция.

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


Синтаксис

лямбда аргументы : выражение

Выражение выполняется и возвращается результат:

Пример

Добавьте 10 к аргументу a , и
вернуть результат:

x = лямбда a: a + 10
печать (x (5))

Попробуй сам »

Лямбда-функции могут принимать любое количество аргументов:

Пример

Умножить аргумент на на аргумент
b и вернуть
результат:

x = лямбда a, b: a * b
print (x (5, 6))

Попробуй сам »

Пример

Суммировать аргумент a ,
b и c и
вернуть
результат:

x = лямбда a, b, c: a + b + c
print (x (5, 6,
2))

Попробуй сам »



Зачем нужны лямбда-функции?

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

Допустим, у вас есть определение функции, которое принимает один аргумент, и этот аргумент
будет умножено на неизвестное число:

def myfunc (n):
вернуть лямбда a: a * n

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

Пример

определение myfunc (n):
вернуть лямбда a: a * n

mydoubler = myfunc (2)

print (mydoubler (11))

Попробуй сам »

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

Пример

определение myfunc (n):
вернуть лямбда a: a * n

mytripler = myfunc (3)

print (mytripler (11))

Попробуй сам »

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

Пример

определение myfunc (n):
вернуть лямбда a: a * n

mydoubler = myfunc (2)
mytripler = myfunc (3)

print (mydoubler (11))

принт (mytripler (11))

Попробуй сам »

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



Как использовать лямбда-функции Python — настоящий Python

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

Лямбда-выражения 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):
... вернуть x
  

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

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

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

  • Ключевое слово: лямбда
  • Связанная переменная: x
  • Кузов: x

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

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

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

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

>>>

  >>> (лямбда x: x + 1) (2)
3
  

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

.

  (лямбда x: x + 1) (2) = лямбда 2: 2 + 1
                     = 2 + 1
                     = 3
  

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

>>>

  >>> add_one = лямбда x: x + 1
>>> add_one (2)
3
  

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

  def add_one (x):
    вернуть x + 1
  

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

>>>

  >>> full_name = lambda first, last: f'Полное имя: {first.title ()} {last.title ()} '
>>> полное_имя ('гидо', 'ван россум')
'Полное имя: Гвидо Ван Россум'
  

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

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

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

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

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

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

>>>

  >>> лямбда x, y: x + y
  

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

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

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

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

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

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

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

>>>

  >>> (лямбда x, y: x + y) (2, 3)
5
  

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

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

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

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

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

>>>

  >>> high_ord_func = лямбда x, func: x + func (x)
>>> high_ord_func (2, лямбда x: x * x)
6
>>> high_ord_func (2, лямбда 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:

>>>

  >>> импорт
>>> add = лямбда x, y: x + y
>>> введите (добавить)
<класс 'функция'>
>>> dis.dis (добавить)
  1 0 LOAD_FAST 0 (x)
              2 LOAD_FAST 1 (г)
              4 BINARY_ADD
              6 RETURN_VALUE
>>> добавить
<функция <лямбда> в 0x7f30c6ce9ea0>
  

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

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

>>>

  >>> импорт
>>> def add (x, y): вернуть x + y
>>> введите (добавить)
<класс 'функция'>
>>> dis.dis (добавить)
  1 0 LOAD_FAST 0 (x)
              2 LOAD_FAST 1 (г)
              4 BINARY_ADD
              6 RETURN_VALUE
>>> добавить
<функция добавить в 0x7f30c6ce9f28>
  

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

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

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

.
>>>

  >>> div_zero = лямбда x: x / 0
>>> div_zero (2)
Отслеживание (последний вызов последний):
    Файл "", строка 1, в 
    Файл "", строка 1, в 
ZeroDivisionError: деление на ноль
  

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

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

>>>

  >>> def div_zero (x): вернуть x / 0
>>> div_zero (2)
Отслеживание (последний вызов последний):
    Файл "", строка 1, в 
    Файл "", строка 1, в div_zero
ZeroDivisionError: деление на ноль
  

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

Синтаксис

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

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

Нет выписки

Лямбда-функция не может содержать операторов.SyntaxError: недопустимый синтаксис

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

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

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

>>>

  >>> (лямбда x:
... (x% 2 и «нечетное» или «четное»)) (3)
'странный'
  

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

Обозначения типа

Если вы начали использовать подсказки типов, которые теперь доступны в Python, у вас есть еще одна веская причина предпочесть обычные функции лямбда-функциям Python.Ознакомьтесь с проверкой типов Python (Руководство), чтобы узнать больше о подсказках типов 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
  Файл "", строка 1
    лямбда сначала: str, last: str: first.title () + "" + last.title () -> str

SyntaxError: недопустимый синтаксис
  

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

IIFE

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

>>>

  >>> (лямбда x: x * x) (3)
9
  

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

Аргументы

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

  • Позиционные аргументы
  • Именованные аргументы (иногда называемые аргументами ключевого слова)
  • Переменный список аргументов (часто обозначается как varargs )
  • Переменный список аргументов ключевого слова
  • Аргументы только для ключевых слов

Следующие примеры иллюстрируют варианты, доступные вам для передачи аргументов лямбда-выражениям:

>>>

  >>> (лямбда x, y, z: x + y + z) (1, 2, 3)
6
>>> (лямбда x, y, z = 3: x + y + z) (1, 2)
6
>>> (лямбда x, y, z = 3: x + y + z) (1, y = 2)
6
>>> (лямбда * аргументы: сумма (аргументы)) (1,2,3)
6
>>> (лямбда ** kwargs: sum (kwargs.values ​​())) (один = 1, два = 2, три = 3)
6
>>> (лямбда x, *, y = 0, z = 0: x + y + z) (1, y = 2, z = 3)
6
  

Декораторы

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

  def some_decorator (f):
    def обертывания (* args):
        print (f "Вызов функции '{f .__ name__}'")
        вернуть f (аргументы)
    возвратные обертки

@some_decorator
def Decorated_function (x):
    print (f "С аргументом '{x}'")
  

В приведенном выше примере some_decorator () — это функция, которая добавляет поведение к Decorated_function () , так что вызов decor_function ("Python") приводит к следующему результату:

  Вызов функции «Decorated_function»
С аргументом Python
  

Decorated_function () печатает только с аргументом «Python» , но декоратор добавляет дополнительное поведение, которое также печатает Вызов функции «Decorated_function» .

К лямбде можно применить декоратор. Хотя невозможно украсить лямбда синтаксисом @decorator , декоратор — это просто функция, поэтому он может вызывать лямбда-функцию:

  1 # Определение декоратора
 2def trace (f):
 3 def wrap (* args, ** kwargs):
 4 print (f "[TRACE] func: {f .__ name__}, args: {args}, kwargs: {kwargs}")
 5 вернуть f (* args, ** kwargs)
 6
 7 возвратная пленка
 8
 9 # Применение декоратора к функции
10 @ след
11def add_two (x):
12 возврат x + 2
13
14 # Вызов декорированной функции
15add_two (3)
16
17 # Применение декоратора к лямбде
18print ((след (лямбда x: x ** 2)) (3))
  

add_two () , украшенный @trace в строке 11, вызывается с аргументом 3 в строке 15.Напротив, в строке 18 сразу задействуется лямбда-функция, которая встраивается в вызов trace () , декоратора. Когда вы выполните приведенный выше код, вы получите следующее:

  [TRACE] func: add_two, args: (3,), kwargs: {}
[TRACE] func: <лямбда>, аргументы: (3,), kwargs: {}
9
  

Посмотрите, как, как вы уже видели, имя лямбда-функции отображается как , тогда как add_two четко идентифицируется для нормальной функции.

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

.

  список (карта (трассировка (лямбда x: x * 2), диапазон (3)))
  

Первый аргумент map () — это лямбда, умножающая свой аргумент на 2 . Эта лямбда украшена следом () . При выполнении приведенный выше пример выводит следующее:

  [TRACE] Вызов  с аргументами (0,) и kwargs {}
[TRACE] Вызов  с аргументами (1,) и kwargs {}
[TRACE] Вызов  с аргументами (2,) и kwargs {}
[0, 2, 4]
  

Результат [0, 2, 4] — это список, полученный умножением каждого элемента диапазона (3) .А пока рассмотрим диапазон (3) , эквивалентный списку [0, 1, 2] .

Вы увидите карту () более подробно на карте.

Лямбда также может быть декоратором, но это не рекомендуется. Если вам нужно это сделать, обратитесь к PEP 8, Рекомендации по программированию.

Чтобы узнать больше о декораторах Python, ознакомьтесь с Primer on Python Decorators.

Закрытие

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

Понятия лямбда-выражений и замыканий не обязательно связаны, хотя лямбда-функции могут быть замыканиями точно так же, как обычные функции могут быть замыканиями. В некоторых языках есть специальные конструкции для замыкания или лямбда (например, Groovy с анонимным блоком кода в качестве объекта Closure) или лямбда-выражения (например, лямбда-выражение Java с ограниченным параметром закрытия).

Вот замыкание, построенное с помощью обычной функции Python:

  1def outer_func (x):
 2 года = 4
 3 def inner_func (z):
 4 print (f "x = {x}, y = {y}, z = {z}")
 5 вернуть x + y + z
 6 возвратите inner_func
 7
 8 для i в диапазоне (3):
 9 закрытие = external_func (я)
10 print (f "закрытие ({i + 5}) = {закрытие (i + 5)}")
  

outer_func () возвращает inner_func () , вложенную функцию, которая вычисляет сумму трех аргументов:

  • x передается в качестве аргумента функции outer_func () .
  • y — переменная, локальная для outer_func () .
  • z — это аргумент, переданный в inner_func () .

Для проверки поведения outer_func () и inner_func () , outer_func () вызывается три раза в цикле for , который выводит следующее:

  х = 0, у = 4, г = 5
закрытие (5) = 9
х = 1, у = 4, г = 6
закрытие (6) = 11
х = 2, у = 4, г = 7
закрытие (7) = 13
  

В строке 9 кода inner_func () , возвращенный вызовом outer_func () , привязан к замыканию имени .В строке 5 inner_func () захватывает x и y , потому что у него есть доступ к своей среде внедрения, так что при вызове закрытия он может работать с двумя свободными переменными x и y. .

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

  1def outer_func (x):
 2 года = 4
 3 вернуть лямбда z: x + y + z
 4
 5 для i в диапазоне (3):
 6 закрытие = external_func (i)
 7 print (f "закрытие ({i + 5}) = {закрытие (i + 5)}")
  

Когда вы выполните приведенный выше код, вы получите следующий результат:

 закрытие  (5) = 9
закрытие (6) = 11
закрытие (7) = 13
  

В строке 6 outer_func () возвращает лямбду и присваивает ее закрытию переменной .В строке 3 тело лямбда-функции ссылается на x и y . Переменная y доступна во время определения, тогда как x определяется во время выполнения, когда вызывается outer_func () .

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

Время оценки

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

Сначала проверьте сценарий, используя обычную функцию:

>>>

  1 >>> def wrap (n):
 2 ... def f ():
 3 ... печать (n)
 4 ... вернуть f
 5 ...
 6 >>> числа = 'один', 'два', 'три'
 7 >>> funcs = []
 8 >>> для n цифрами:
 9 ... функц.добавить (обернуть (n))
10 ...
11 >>> для f в функциях:
12 ... f ()
13 ...
14one
15два
16три
  

В нормальной функции n оценивается во время определения, в строке 9, когда функция добавляется в список: funcs.append (wrap (n)) .

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

>>>

  1 >>> числа = 'один', 'два', 'три'
 2 >>> funcs = []
 3 >>> для n цифрами:
 4... funcs.append (лямбда: print (n))
 5 ...
 6 >>> для f в функциях:
 7 ... f ()
 8 ...
 9три
10три
11три
  

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

Чтобы решить эту проблему, вы можете назначить свободную переменную во время определения следующим образом:

>>>

  1 >>> числа = 'один', 'два', 'три'
 2 >>> funcs = []
 3 >>> для n цифрами:
 4 ... funcs.append (лямбда n = n: print (n))
 5 ...
 6 >>> для f в функциях:
 7 ... f ()
 8 ...
 9one
10два
11три
  

Лямбда-функция Python ведет себя как обычная функция в отношении аргументов. Следовательно, лямбда-параметр может быть инициализирован значением по умолчанию: параметр n принимает внешние n в качестве значения по умолчанию.Лямбда-функцию Python можно было бы записать как лямбда x = n: print (x) и получить тот же результат.

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

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

лямбда-выражения Python можно тестировать аналогично обычным функциям. Можно использовать как unittest , так и doctest .

unittest

Модуль unittest обрабатывает лямбда-функции Python аналогично обычным функциям:

  импортный unittest

addtwo = лямбда x: x + 2

класс LambdaTest (unittest.Прецедент):
    def test_add_two (сам):
        self.assertEqual (addtwo (2), 4)

    def test_add_two_point_two (сам):
        self.assertEqual (addtwo (2.2), 4.2)

    def test_add_three (сам):
        # Должен потерпеть неудачу
        self.assertEqual (addtwo (3), 6)

если __name__ == '__main__':
    unittest.main (многословие = 2)
  

LambdaTest определяет тестовый пример с тремя методами тестирования, каждый из которых выполняет тестовый сценарий для addtwo () , реализованного как лямбда-функция.Выполнение файла Python lambda_unittest.py , содержащего LambdaTest , дает следующее:

  $ питон lambda_unittest.py
test_add_three (__main __. LambdaTest) ... FAIL
test_add_two (__main __. LambdaTest) ... хорошо
test_add_two_point_two (__main __. LambdaTest) ... хорошо

================================================== ====================
ОТКАЗ: test_add_three (__main __. LambdaTest)
-------------------------------------------------- --------------------
Отслеживание (последний вызов последний):
  Файл "lambda_unittest.py ", строка 18, в test_add_three
    self.assertEqual (addtwo (3), 6)
AssertionError: 5! = 6

-------------------------------------------------- --------------------
Выполнить 3 теста за 0,001 с

ВЫПОЛНЕНО (отказов = 1)
  

Как и ожидалось, у нас есть два успешных тестовых примера и один сбой для test_add_three : результат 5 , но ожидаемый результат был 6 . Этот сбой вызван преднамеренной ошибкой в ​​тестовом примере. Изменение ожидаемого результата с 6 на 5 удовлетворит все тесты LambdaTest .

доктест

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

  addtwo = лямбда x: x + 2
addtwo .__ doc__ = "" "Добавьте 2 к числу.
    >>> addtwo (2)
    4
    >>> addtwo (2.2)
    4.2
    >>> addtwo (3) # Должен потерпеть неудачу
    6
    "" "

если __name__ == '__main__':
    импорт документов
    doctest.testmod (verbose = True)
  

doctest в комментарии документа лямбда addtwo () описывает те же тестовые случаи, что и в предыдущем разделе.

Когда вы выполняете тесты через doctest.testmod () , вы получаете следующее:

  $ питон lambda_doctest.py
Пытающийся:
    addtwo (2)
Ожидая:
    4
Ok
Пытающийся:
    addtwo (2.2)
Ожидая:
    4.2
Ok
Пытающийся:
    addtwo (3) # Должен потерпеть неудачу
Ожидая:
    6
************************************************* ********************
Файл "lambda_doctest.py", строка 16, в __main __. Addtwo
Неудачный пример:
    addtwo (3) # Должен потерпеть неудачу
Ожидал:
    6
Есть:
    5
1 предмет не прошел тестов:
    __главный__
************************************************* ********************
По 1 предмету были сбои:
   1 из 3 в __main __. Addtwo
3 теста по 2 задания.
2 пройдены и 1 не пройден.
*** Тест не пройден *** 1 сбой. 

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

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

Для исчерпывающего обзора модульного тестирования в Python вы можете обратиться к Приступая к работе с тестированием в Python.

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

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

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

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

  • Он не соответствует руководству по стилю Python (PEP 8)
  • Это громоздко и трудно читать.
  • Это излишне умно за счет трудной читаемости.

Вызов исключения

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

>>>

  >>> def throw (ex): поднять ex
>>> (лямбда: throw (Exception ('Произошло что-то плохое'))) ()
Отслеживание (последний вызов последний):
    Файл "", строка 1, в 
    Файл "", строка 1, в 
    Файл "", строка 1, в throw
Исключение: произошло что-то плохое.
  

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

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

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

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

>>>

  >>> (lambda _: list (map (lambda _: _ // 2, _))) ([1,2,3,4,5,6,7,8,9,10] )
[0, 1, 1, 2, 2, 3, 3, 4, 4, 5]
  

Знак подчеркивания ( _ ) указывает на переменную, на которую не нужно ссылаться явно.Но в этом примере три _ относятся к разным переменным. Первоначальное обновление этого лямбда-кода могло состоять в том, чтобы назвать переменные:

>>>

  >>> (lambda some_list: list (map (lambda n: n // 2,
                                some_list))) ([1,2,3,4,5,6,7,8,9,10])
[0, 1, 1, 2, 2, 3, 3, 4, 4, 5]
  

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

>>>

  >>> def div_items (some_list):
      div_by_two = лямбда n: n // 2
      вернуть карту (div_by_two, some_list)
>>> список (div_items ([1,2,3,4,5,6,7,8,9,10])))
[0, 1, 1, 2, 2, 3, 3, 4, 4, 5]
  

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

Классы Python

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

  класс Автомобиль:
    "" "Автомобиль с методами как лямбда-функциями." ""
    def __init __ (я, бренд, год):
        self.brand = бренд
        self.year = год

    brand = property (лямбда self: getattr (self, '_brand'),
                     лямбда self, значение: setattr (self, '_brand', value))

    год = свойство (лямбда-я: getattr (self, '_year'),
                    лямбда self, значение: setattr (self, '_year', value))

    __str__ = лямбда self: f '{self.brand} {self.year} '# 1: ошибка E731

    honk = lambda self: print ('Honk!') # 2: ошибка E731
  

Запуск такого инструмента, как flake8 , инструмента обеспечения соблюдения правил стиля, отобразит следующие ошибки для __str__ и honk :

  E731 не назначают лямбда-выражение, используйте def
  

Хотя flake8 не указывает на проблему использования лямбда-функций Python в свойствах, их трудно читать и они подвержены ошибкам из-за использования нескольких строк, таких как '_brand' и '_year '.

Ожидается, что правильная реализация __str__ будет выглядеть следующим образом:

  def __str __ (сам):
    return f '{self.brand} {self.year}'
  

марка запишется следующим образом:

  @property
def brand (self):
    вернуть self._brand

@ brand.setter
def brand (self, value):
    self._brand = значение
  

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

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

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

  • Проблемы с читабельностью
  • Навязывание функционального мышления
  • Тяжелый синтаксис с ключевым словом лямбда

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

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

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

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

>>>

  >>> список (map (lambda x: x.upper (), ['кошка', 'собака', 'корова']))
["КОШКА", "СОБАКА", "КОРОВА"]
>>> list (filter (lambda x: 'o' in x, ['cat', 'dog', 'cow']))
['собака', 'корова']
>>> из импорта functools уменьшить
>>> reduce (lambda acc, x: f '{acc} | {x}', ['кошка', 'собака', 'корова'])
'кошка | собака | корова'
  

Возможно, вам придется прочитать код, похожий на приведенные выше примеры, хотя и с более релевантными данными. По этой причине важно распознавать эти конструкции. Тем не менее, у этих конструкций есть эквивалентные альтернативы, которые считаются более питоническими.В разделе «Альтернативы лямбдам» вы узнаете, как преобразовывать функции высшего порядка и соответствующие лямбды в другие, более идиоматические формы.

Ключевые функции

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

  • sort () : метод списка
  • отсортировано () , мин. () , макс. () : встроенных функций
  • nlargest () и nsmallest () : в модуле алгоритма очереди кучи heapq

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

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

>>>

  >>> ids = ['id1', 'id2', 'id30', 'id3', 'id22', 'id100']
>>> print (sorted (ids)) # Лексикографическая сортировка
['id1', 'id100', 'id2', 'id22', 'id3', 'id30']
>>> sorted_ids = sorted (ids, key = lambda x: int (x [2:])) # Целочисленная сортировка
>>> печать (sorted_ids)
['id1', 'id2', 'id3', 'id22', 'id30', 'id100']
  

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

UI-фреймворки, такие как Tkinter, wxPython или.NET Windows Forms с IronPython используют лямбда-функции для сопоставления действий в ответ на события пользовательского интерфейса.

Наивная программа Tkinter ниже демонстрирует использование лямбда , назначенного команде кнопки Reverse :

  импорт tkinter as tk
import sys

окно = tk.Tk ()
window.grid_columnconfigure (0, вес = 1)
window.title ("Лямбда")
window.geometry ("300x100")
label = tk.Label (window, text = "Лямбда-исчисление")
label.grid (столбец = 0, строка = 0)
кнопка = тк.Кнопка(
    окно,
    text = "Обратный",
    command = lambda: label.configure (text = label.cget ("текст") [:: - 1]),
)
button.grid (столбец = 0, строка = 1)
window.mainloop ()
  

При нажатии кнопки Reverse запускается событие, которое запускает лямбда-функцию, меняя метку с Lambda Calculus на suluclaC adbmaL *:

И wxPython, и IronPython на платформе .NET используют схожий подход к обработке событий. Обратите внимание, что лямбда — это один из способов обработки событий срабатывания, но для той же цели можно использовать функцию.В конечном итоге он оказывается самодостаточным и менее подробным, чтобы использовать лямбда , когда количество необходимого кода очень мало.

Чтобы изучить wxPython, прочтите статью Как создать приложение Python с графическим интерфейсом пользователя с помощью wxPython.

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

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

таймит

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

>>>

  >>> из timeit import timeit
>>> timeit ("factorial (999)", "from math import factorial", number = 10)
0.0013087529951008037
  

Когда оператор передается в виде строки, timeit () требуется полный контекст. В приведенном выше примере это обеспечивается вторым аргументом, который устанавливает среду, необходимую для основной функции, для синхронизации. В противном случае возникнет исключение NameError .

Другой подход — использовать лямбда :

>>>

  >>> из математического импорта факториала
>>> timeit (лямбда: факториал (999), число = 10)
0.0012704220062005334
  

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

Патч для обезьян

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

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

  из contextlib import contextmanager
секреты импорта

def gen_token ():
    "" "Создать случайный токен." ""
    return f'TOKEN_ {secrets.token_hex (8)} '

@contextmanager
def mock_token ():
    "" "Менеджер контекста, чтобы обезьяна исправляла секреты.token_hex
    функция во время тестирования.
    "" "
    default_token_hex = секреты.token_hex
    secretts.token_hex = lambda _: 'feedfacecafebeef'
    урожай
    secretts.token_hex = default_token_hex

def test_gen_key ():
    "" "Проверить случайный токен." ""
    с mock_token ():
        assert gen_token () == f "ТОКЕН _ {'feedfacecafebeef'}"

test_gen_key ()
  

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

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

Среды модульного тестирования

, такие как unittest и pytest , выводят эту концепцию на более высокий уровень сложности.

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

  секреты импорта

def gen_token ():
    return f'TOKEN_ {secrets.token_hex (8)} '

def test_gen_key (патч обезьяны):
    monkeypatch.setattr ('secrets.token_hex', lambda _: 'feedfacecafebeef')
    assert gen_token () == f "ТОКЕН _ {'feedfacecafebeef'}"
  

С прибором pytest monkeypatch , секретов.token_hex () перезаписывается лямбда-выражением, которое возвращает детерминированное значение, feedfacecafebeef , что позволяет проверить тест. Приспособление pytest monkeypatch позволяет вам контролировать объем переопределения. В приведенном выше примере вызов secrets.token_hex () в последующих тестах без использования исправлений обезьяны приведет к нормальной реализации этой функции.

Выполнение теста pytest дает следующий результат:

  $ pytest test_token.py -v
============================= Начинается тестовая сессия ================== ============
платформа Linux - Python 3.7.2, pytest-4.3.0, py-1.8.0, pluggy-0.9.0
cachedir: .pytest_cache
корневой каталог: / home / andre / AB / tools / bpython, inifile:
собрано 1 предмет

test_token.py::test_gen_key ПРОЙДЕН [100%]

=========================== 1 пройдено за 0,01 секунды ================== =========
  

Тест проходит, так как мы подтвердили, что gen_token () был задействован, и результаты были ожидаемыми в контексте теста.

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

Хотя есть веские причины использовать лямбда , есть случаи, когда его использование осуждается. Итак, какие есть альтернативы?

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

Чтобы узнать больше о понимании списков, прочтите статью Когда использовать понимание списков в Python.Чтобы узнать больше о выражениях генератора, ознакомьтесь с разделом Как использовать генераторы и yield в Python.

Карта

Встроенная функция map () принимает функцию в качестве первого аргумента и применяет ее к каждому из элементов своего второго аргумента, итерации . Примерами итераций являются строки, списки и кортежи. Для получения дополнительной информации об итераторах и итераторах ознакомьтесь с Iterables и Iterators.

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

>>>

  >>> list (map (lambda x: x.capitalize (), ['cat', 'dog', 'cow']))
["Кошка", "Собака", "Корова"]
  

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

Использование понимания списка устраняет необходимость определения и вызова лямбда-функции:

>>>

  >>> [x.capitalize () вместо x в ['кошка', 'собака', 'корова']]
["Кошка", "Собака", "Корова"]
  

Фильтр

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

>>>

  >>> четное = лямбда x: x% 2 == 0
>>> список (фильтр (даже, диапазон (11)))
[0, 2, 4, 6, 8, 10]
  

Обратите внимание, что filter () возвращает итератор, следовательно, необходимо вызывать встроенный тип list , который создает список с учетом итератора.

Реализация, использующая конструкцию понимания списка, дает следующее:

>>>

  >>> [x для x в диапазоне (11), если x% 2 == 0]
[0, 2, 4, 6, 8, 10]
  

уменьшить

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

Чтобы применить reduce () к списку пар и вычислить сумму первого элемента каждой пары, вы можете написать это:

>>>

  >>> import functools
>>> пары = [(1, 'a'), (2, 'b'), (3, 'c')]
>>> functools.reduce (лямбда-совпадение, пара: совпадение + пара [0], пары, 0)
6
  

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

>>>

  >>> пары = [(1, 'a'), (2, 'b'), (3, 'c')]
>>> sum (x [0] для x попарно)
6
  

Немного другое и, возможно, более чистое решение устраняет необходимость явного доступа к первому элементу пары и вместо этого использует распаковку:

>>>

  >>> пары = [(1, 'a'), (2, 'b'), (3, 'c')]
>>> сумма (x вместо x, _ попарно)
6
  

Использование подчеркивания ( _ ) — это соглашение Python, указывающее, что вы можете игнорировать второе значение пары.

sum () принимает уникальный аргумент, поэтому выражение генератора не обязательно заключать в круглые скобки.

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

PEP 8, руководство по стилю кода Python, гласит:

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

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

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

Помимо узких рамок Python lambda , Как написать красивый код Python с помощью PEP 8 — отличный ресурс, который вы, возможно, захотите проверить относительно стиля кода в Python.

Заключение

Теперь вы знаете, как использовать функции Python lambda и можете:

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

Если у вас есть склонность к математике, вы можете повеселиться, исследуя увлекательный мир лямбда-исчисления.

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

Первая Второй Третий Forth
0 1 2 3 6
1 4 5 6 120

Первая Второй Третий Forth
0 1 4 9 36
1 16 25 36 14400