Что такое функции высшего порядка в Python
Функции высшего порядка — это функции, которые принимают другие функции в качестве аргументов или возвращают функции как результат. Это позволяет создавать более модульный и масштабируемый код, поскольку функции могут быть легко комбинированы и переиспользованы. В Python, функции являются объектами первого класса, что означает, что они могут быть переданы и возвращены, как и любые другие объекты.
Примеры функций высшего порядка
Рассмотрим несколько примеров функций высшего порядка в Python.
Функция map
Функция map принимает функцию и итерируемый объект (например, список) и применяет данную функцию ко всем элементам итерируемого объекта. В результате получаем объект-итератор, который содержит результаты применения функции.
def square(x): return x * x numbers = [1, 2, 3, 4, 5] squares = map(square, numbers) print(list(squares)) # [1, 4, 9, 16, 25]
Функция filter
Функция filter принимает функцию-предикат и итерируемый объект, проходит по каждому элементу итерируемого объекта и возвращает новый итератор, состоящий только из тех элементов, для которых функция-предикат вернула True .
def is_even(x): return x % 2 == 0 numbers = [1, 2, 3, 4, 5, 6] even_numbers = filter(is_even, numbers) print(list(even_numbers)) # [2, 4, 6]
Функция, возвращающая функцию
Функции высшего порядка могут также возвращать другие функции. Это полезно, когда вы хотите создать функцию с некоторыми предустановленными параметрами.
def make_multiplier(factor): def multiplier(x): return x * factor return multiplier double = make_multiplier(2) triple = make_multiplier(3) print(double(4)) # 8 print(triple(4)) # 12
Замыкания и декораторы
Функции высшего порядка тесно связаны с понятием замыканий и декораторов в Python. Замыкание — это функция, которая «запоминает» окружение, в котором была определена, даже если оно уже не существует. Декораторы — это способ изменить поведение функции или класса, обернув его в другую функцию или класс.
def make_greeting_decorator(greeting): def decorator(func): def wrapper(name): return f", !" return wrapper return decorator @make_greeting_decorator("Hello") def greet(name): return name print(greet("John")) # Hello, John!
В этом примере, мы создали декоратор make_greeting_decorator , который добавляет приветствие к результату вызова функции greet .
Теперь вы знаете, что такое функции высшего порядка в Python, и как их использовать для создания более модульного и масштабируемого кода. 😊
Введение в Python Functools
Python – высокоуровневый объектно-ориентированный язык программирования. Одно из самых больших преимуществ Python заключается в том, что в нем есть специальный функционал, который позволяет писать переиспользуемый код с помощью встроенных инструментов языка.
Functools – это библиотека Python, которая предназначена для работы с функциями высшего порядка. Такие функции могут принимать в себя другие функции и возвращать функции. Они помогают разработчиком писать код, который можно переиспользовать. Функции можно использовать или расширять, не переписывая их полностью. Модуль functools в Python предоставляет различные инструменты, которые позволяют добиться описанного эффекта. Например, следующие:
Функция partial в Python
В модуле functools одним из важнейших инструментов считается функция partial. С помощью partial функции можно заменить существующую функцию, которой уже переданы аргументы. Более того, мы также можем создать новую версию функции, добавив качественную документацию.
Мы можем создавать новые функции, передавая частичные аргументы. Также мы можем заморозить некоторые аргументы функции, что приведет к появлению нового объекта. Еще один способ представить partial, заключается в том, что с ее помощью мы можем создать функцию со значениями по умолчанию. Partial поддерживает ключевые слова и позиционные аргументы в качестве фиксированных.
Давайте разберемся на примерах.
Как создать функцию partial?
Чтобы создать partial-функцию, используйте partial() из библиотеки functools. Пишется она следующим образом:
partial(func, /, *args, ** kwargs)
Так вы создадите partial функцию, которая вызовет func, передав ей фиксированные ключевые слова и позиционные аргументы. Здесь обычно передаются несколько необходимых аргументов для вызова функции func. Остальные аргументы передаются в *args и **kwargs.
Допустим, функция ниже складывает два числа:
def multiply(x, y): return x * y
Теперь рассмотрим случай, когда нам понадобилось удвоить или утроить заданное число. В таком случае новые функции мы определим, как показано ниже:
def multiply(x, y): return x * y def doubleNum(x): return multiply(x, 2) def tripleNum(x): return multiply(x, 3)
Когда сценария работы функции всего 2-3, конечно, логичнее сделать, как показано выше. Но когда нужно написать еще 100 таких функций, то смысла переписывать один и тот же код столько раз нет. Здесь нам и пригодятся partial функции. Чтобы ими воспользоваться, во-первых, нам нужно импортировать partial из Functools.
from functools import partial def multiply(x, y): return x * y doubleNum = partial(multiply, 2) tripleNum = partial(multiply, 3) Print(doubleNum(10)) Output: 20
Как видно из примера, значения по умолчанию будут заменены переменными слева. Вместо x будет 2, а вместо y будет 10 при вызове doubleNum(10). В этом примере порядок не будет иметь принципиального значения, но в других вариантах использования он может иметь значение. Давайте рассмотрим пример на этот случай, чтобы понять порядок замены переменных.
from functools import partial def orderFunc(a,b,c,d): return a*4 + b*3 + c*2 + d result = partial(orderFunc,5,6,7) print(result(8)) Output: 60
Полное упорядочивание
У нас появилась функция orderFunc() , в которой происходит умножение a на 4, b на 3, c на 2 и добавление d к сумме значений.
Мы создали partial функцию result() , которая вызывает orderFunc() со значениями 5, 6 и 7. Теперь значения 5, 6 и 7 будут заменять переменные a , b и c соответственно. На место переменной d встанет 8, так как она передается при вызове result() . В результате получится (4*5 + 6*3 + 7*2 + 8) = 60.
В этом случае порядок передаваемых значений будет иметь значение, поскольку если изменится порядок, то изменится и результат. Чтобы зафиксировать переменные, можно использовать ключевые слова вместо позиционных аргументов. Давайте перепишем код приведенный выше используя ключевые слова в качестве аргументов.
from functools import partial def orderFunc(a,b,c,d): return a*4 + b*3 + c*2 + d result = partial(orderFunc,c=5,d=6) print(result(8,4)) Output: 60
Здесь мы зафиксировали значение 5 за переменной c и 6 за переменной d . Вместо переменных a и b встанут значения 8 и 4. В результате получится (8*4+4*3+5*2+6) = 60.
Partial функцию можно определить в цикле и использовать для повторяющихся вычислений. Давайте рассмотрим пример:
from functools import partial def add(x,y): return x + y add_partials = [] for i in range (1, 10): function = partial(add, i) add_partials.append(function) print('Sum of <> and 2 is <>'.format(i,add_partials[i-1](2))) Output: Sum of 1 and 2 is 3 Sum of 2 and 2 is 4 Sum of 3 and 2 is 5 Sum of 4 and 2 is 6 Sum of 5 and 2 is 7 Sum of 6 and 2 is 8 Sum of 7 and 2 is 9 Sum of 8 and 2 is 10 Sum of 9 and 2 is 11
В этом примере мы будем суммировать определенный диапазон значений с 2, переиспользуя имеющуюся функцию. Мы можем вызвать partial в цикле и использовать ее функционал для вычисления сумм. Как видно из значений на выходе, у нас есть цикл от 1 до 10 и все значения в этом промежутке прибавляются к 2 с помощью функции partial, которая вызывает функцию сложения.
Метаданные
Несмотря на то, что partial функции являются независимыми, они хранят память (метаданные) функции, которую они расширяют.
from functools import partial def add(x,y): return x + y # create a new function that multiplies by 2 result = partial(add,y=5) print(result.func) print(result.keywords) Output:
Первый вызов func передаст имя функции и ее адрес в памяти, а второй вызов с keywords передаст ключевые слова в функцию. Таким образом функции partial можно назвать самодокументирующимися с помощью метаданных, которые они получают от расширяемой функции.
update_wrapper для partial
Мы можем обновлять метаданные функции с помощью другого инструмента из functools. Update_wrapper – это инструмент, который можно использовать для обновления метаданных функции. Давайте разберемся с ним на примере.
def multiply(x, y): """Test string.""" return x * y result = functools.partial(multiply, y=2) try: print ('Function Name:'+result.__name__) except AttributeError: print('Function Name: __no name__') print ('Function Doc:'+result.__doc__) print('Updating wrapper:') functools.update_wrapper(result, multiply) print ('Function Name:'+result.__name__) print ('Function Doc:'+result.__doc__) Output: Function Name: __no name__ Function Doc:partial(func, *args, **keywords) - new function with partial application of the given arguments and keywords. Updating wrapper: Function Name: multiply Function Doc: Test string.
Теперь как видно из выходных данных, до использования обертки (wrapper) у функции не было закрепленного за ней имени или документа. Как только мы обновили name и doc функции с помощью update_wrapper , в выводе увидели соответствующий результат.
Заключение
С помощью functools мы можем избавиться от избыточного кода и увеличить возможности переиспользования кода в Python. Чем чаще вы будете использовать функцию partial, тем больше вариантов использования будете открывать. Экспериментируйте и получайте от этого удовольствие!