Функции высшего порядка в Python: замыкания и декораторы
Вступление
Функция называется функцией высшего порядка, если она содержит другие функции в качестве параметров или возвращает функции. Это важная концепция функционального программирования.
Python, как очень гибкий язык программирования, поддерживает использование функций более высокого порядка.
В Python есть несколько встроенных функций высшего порядка, и мы также можем сами определять функции высшего порядка.
В этом посте он будет представлен встроенными функциями Python и объяснен, как он работает, на простых примерах.
Функции карты, уменьшения и фильтрации
Эти три функции обычно используются встроенными функциями высшего порядка в Python. Многие другие языки программирования также имеют встроенные функции, подобные им. Правильное применение этих трех функций может помочь нам избежать слишком большого количества циклов for в нашем коде и сделать его более элегантным и читаемым.
Все они получают два параметра, что делает их немного похожими. Однако они имеют разные последствия и сценарии использования. Давай посмотрим на них.
Функция карты
map() function получает два параметра, один из которых является функцией, а другой — Iterable. Он применяет инициализированную функцию к каждому элементу последовательности по очереди и возвращает результат в виде нового Итератора.
Например, если нам нужно вычислить квадрат каждого числа в списке, мы можем использовать map():
Фактически, мы можем получить его всего одной строкой с лямбда-функцией:
r = map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])
Очевидно, что использование функции map() может сделать наш код более элегантным и понятным, чем использование цикла for.
Другой пример: используйте функцию map() , чтобы изменить нестандартное английское имя на каноническое:
Метод reduce() также имеет два параметра, один из которых является функцией, а другой — Iterable. Он применяет функцию к последовательности, эта функция должна получать два параметра, reduce продолжает результат и выполняет совокупное вычисление со следующим элементом последовательности. Наконец, он возвращает результат кумулятивного расчета.
Последствия использования функции reduce() такие же, как и при повторном использовании функции для Iterable.
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
Например, если мы хотим преобразовать список ['L','o','n','d','o','n',2,0,2,0] в строку «London2020”, использование reduce — хорошая идея.
Функция фильтра
Подобно map(), функция filter() также получает функцию и последовательность. В отличие от map(), filter() применяет входящую функцию к каждому элементу по очереди, а затем решает, сохранить или отклонить элемент, в зависимости от того, является ли возвращаемое значение True или False.
Например, давайте удалим имена длиннее четырех букв в предыдущем примере:
Как и map() function, filter() function возвращает Iterator, который представляет собой ленивую последовательность, поэтому, чтобы заставить его завершить результаты вычислений, нам нужно использовать list function, чтобы получить все результаты и вернуть список.
Функция сортировки в Python
Сортировка — это также алгоритм, часто используемый в программах. Независимо от того, используется ли пузырьковая сортировка или быстрая сортировка, суть сортировки заключается в сравнении элементов. Если это число, мы можем напрямую сравнивать, но если это строка или «dict»? Следовательно, процесс сравнения должен быть абстрагирован через функции.
- Встроенный в Python метод sorted, который является функцией более высокого порядка, имеет параметр key, принимающий функцию, которая определяет, как сравнивать элементы.
- Например, отсортируем список, включающий имена, по их длине:
- Примечание: есть два способа использования функции сортировки, мы должны понимать различия и правильно их использовать:
№1. Используя sorted напрямую, он вернет новый отсортированный список.
names = sorted(names, key=len)
№2. Вызов функции sort() по последовательности. Это отсортирует его на месте, и ничего не будет возвращено.
names.sort(key=len)
Вернуть функцию
Вышеупомянутые функции получают в качестве своих параметров другие функции. Как мы сказали в начале, функция, возвращающая другую функцию, также называется функцией высшего порядка. Другое формальное название — Закрытие. Давайте посмотрим на пример:
Как показано в приведенном выше примере, вместо того, чтобы возвращать суммированный результат, мы можем вернуть функцию суммы. Это метод ленивой оценки, который дает нам большую гибкость. Мы можем рассчитать результат, когда нам это нужно:
print(func()) # 15
Примечание: func — это всего лишь ссылка (имя) функции, нам нужно вызвать func(), чтобы функция запустилась. В этом примере func сохраняет функцию sum, которая была возвращена функцией lazy_sum. Когда мы вызываем func(), он вычисляет и возвращает окончательный результат суммирования.
Разобравшись с замыканием, легко понять декораторы в Python.
Заключение
Функции высшего порядка в Python дают нам большую гибкость и делают наш код более читабельным и элегантным.
Спасибо, что прочитали, больше руководств по Python здесь:
Использование декораторов в Python
Рассмотрим в статье теоретическое и практическое применение декораторов в Python.
В большинстве случаев люди, занимающиеся программированием в Python, либо никогда не сталкивались с декораторами, либо если и сталкивались, то не могли их понять. В этой статье рассмотрим этот не очень изученный вопрос.
Прежде чем перейти дальше к основной теме, давайте рассмотрим основные моменты.
В Python функции являются объектами первого класса:
- Они могут храниться как переменные
- Они могут быть переданы в качестве аргумента внутри функции
- Они могут быть возвращены из функций в виде значений
- Они действуют точно так же, как переменные
Функции высшего порядка — это функции, которые:
- Принимают другую функцию в качестве аргумента.
- Возвращают другую функцию.
Как раз декораторы используют эти функции.
Простыми словами, декораторы — это то, что украшает нашу функцию.
Декораторы прекрасно заряжают функцию и добавляют к ней дополнительную функциональность. Это та функция, которая обертывает другую функцию и усиливает ее или модифицирует.
Теперь пришло время создать своего собственного декоратора:
def new_decorator(func):
def wrapper(*args, **kwargs):
print(«-«)
func(*args, **kwargs)
print(«*»)
return wrapper
@new_decorator
def hello(greeting, msg):
print(greeting, msg)
hello(
«Привет ребята»,
«Добро пожаловать»,
)
Вывод программы:
—
Привет ребята Добро пожаловать
*
Пошаговое объяснение:
- new_decorator — это функция, содержащая функцию-оболочку.
- wrapper — это функция, которая обертывает в оболочку функцию. Она также добавляет некоторые характеристики к этой функции.
- new_decorator — это фактическая функция, которую мы хотим улучшить.
- Добавляя @ перед функцией, прикрепляем фактическую функцию к декоратору. Здесь @new_decorator — это имя функции декоратора.
- После изучения приведенного выше примера вы можете задаться вопросом, в чем на самом деле заключается потребность этого декоратора или где мы можем его использовать.
- Декораторы могут быть использованы для проверки подлинности пользователя перед выполнением задачи, для расчета времени процесса, создания исключений, отладки кода, замедления кода и многого другого.
- Давайте посмотрим пример, где я хочу вычислить время, затраченное функцией:
from time import time
def perf(func):
def wrapper(*args, **kwargs):
t1 = time()
result = func(*args, **kwargs)
t2 = time()
print(f»Затраченное время {t2-t1} сек»)
return result
return wrapper
@perf
def run():
for i in range(100000000):
i * 6
run()
Вывод программы:
Затраченное время 9.065523862838745 сек
Здесь декоратор вычисляет время до и после выполнения функции. Затем он вычисляет разницу этого времени. Отсюда и отдача затраченного времени.
Несколько декораторов могут быть использованы для одной функции. Они выполняются снизу вверх. Один декоратор также может быть использован для нескольких функций.
Сегодня мы рассмотрели, как использовать декораторы, как их создавать и для чего они используются. Надеюсь статья была легкой в усвоении, а если нет — пишите ваши вопросы в х ????
Функции высокого порядка 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 .
Async processing in java
Теперь вы знаете, что такое функции высшего порядка в Python, и как их использовать для создания более модульного и масштабируемого кода. ????
Источник
Все о функциях высшего порядка в Python
Во-первых, давайте начнем с первого, т. е. с функций высшего порядка, и разберемся в их основах.
Что такое функции высшего порядка в Python?
Функция высшего порядка в Python – это функция, которая имеет другую функцию в качестве аргумента или функцию, которая возвращает другую в качестве возврата на выходе. Функции высшего порядка работают с другими функциями, указанными в программе.
Факт о функции высшего порядка заключается в том, что она применима к обеим функциям, а также к методам, которые принимают функцию в качестве своего параметра или возвращают функцию в качестве их результата. В Python эта концепция функций высшего порядка поддерживается во всех аспектах.
Свойства
Теперь в этом разделе мы обсудим некоторые важные свойства функций высшего порядка, которые также применимы в Python:
- В функции высшего порядка мы можем хранить функцию внутри переменной.
- Может действовать как момент типа объекта.
- Можем вернуть функцию как результат другой функции.
- Передать функцию как параметр или аргумент внутри другой функции.
- Хранить в формате структур данных, таких как списки, хеш-таблицы и т. д.
Способы определения
Поговорим конкретно о функциях высшего порядка Python и о том, как мы можем их определять. Мы обсудим методы и средства, с помощью которых мы будем определять и использовать данные функции в нашей программе Python.
Ниже приведены способы определения функций высшего порядка в коде Python:
- Использование функций как объектов в функции высшего порядка.
- Функции как параметр для другой функции.
- Возврат функции как результат функции высшего порядка.
- Декораторы как функции высшего порядка.
Теперь мы подробно обсудим каждый из приведенных выше методов и узнаем об их реализации в качестве функций высшего порядка в программе Python.
Php or android which is better
Метод 1: использование функций как объектов в функции высшего порядка
В Python мы даже можем присвоить заданную функцию переменной. Это присвоение функции переменной не вызовет фактическую функцию, а вместо этого создаст ссылку на созданную. Таким образом, при назначении функции как объекта переменной создается функция высшего порядка в программе.
Взгляните на следующий пример программы, чтобы узнать о реализации метода, который мы обсуждали выше:
# a default function to take another function parameter def spell(text): # Making text in upper return text.upper() # Taking text as user input text = input(«Enter a text to print it in uppercase and double: «) # Spell function with text print(spell(text)) # Assigning variable with the default function scream = spell # Scream with text variable print(scream(text)) Enter a text to print it in uppercase and double: JavaTPoint JAVATPOINT JAVATPOINT
Метод 2: Функции как параметр для другой функции
По сути, функции Python похожи на объекты Python, и поэтому мы можем использовать функции Python для передачи их в качестве аргумента внутри другой функции, что создаст в программе функцию высшего порядка.
Взгляните на следующую программу, чтобы понять реализацию вышеуказанного метода:
# Default function for making text uppercase def scream(word): return word.upper() # Default function for making text lowercase def spell(word): return word.lower() # A third function that work as a high order function def speak(funct): # Storing the function in a variable in high order function speaking = funct(«Hello Python Developers! You are welcomed to JavaTpoint») print(speaking) # Printing text in uppercase speak(scream) # Printing text in lowercase speak(spell) HELLO PYTHON DEVELOPERS! YOU ARE WELCOMED TO JAVATPOINT hello python developers! you are welcomed to javatpoint
Метод 3: возврат функции как результат функции высшего порядка
Мы также можем вернуть функцию как результат другой функции и объект, это сделает ее функцией высшего порядка.
# A default function for addition def Adding(a): # Nested function with second number def Addition(b): return a + b # addition of two numbers return Addition # Result # Taking both number variable as user input a = int(input(«Enter First Number: «)) b = int(input(«Enter Second Number: «)) # Assigning nested adding function to a variable AddVariable = Adding(a) # Using variable as high order function Result = AddVariable(b) # Printing result print(«Sum of Two numbers given by you is: «, Result) Enter First Number: 24 Enter Second Number: 26 Sum of Two numbers given by you is: 50
Метод 4: декораторы как функции высшего порядка
Мы можем использовать декораторы в качестве функций высшего порядка, как наиболее часто используемую.
Декораторы в Python позволяют нам изменять поведение методов или функций, которые мы определили в программе, а также позволяют нам обернуть функцию внутри другой функции, чтобы расширить поведение обратной или родительской функции. Мы даже можем свернуть функцию внутри другой, даже не изменяя постоянную родительскую функцию.
Python programming beginner pdf
В декораторах Python функция берется в качестве аргумента для другой функции, а затем эти декораторы вызываются внутри функции-обертки. Взгляните на следующий пример синтаксиса декоратора, определенного в программе Python.
# Using a decorator @JTP_Decorator def Python_Decorator(): . .
Приведенный выше синтаксис для декоратора эквивалентен следующему коду Python для функции высшего порядка.
# Using Python default function as Python decorators def Python_Decorator(): . . Python_Decorator = @JTP_Decorator(Python_Decorator)
Мы упомянули @JTP_Decorator как вызываемую функцию внутри функции Python_Decorator() по умолчанию в приведенном выше коде. Нам нужно будет добавить дополнительный код в эту структуру, и мы получим в результате функцию-оболочку.
Взгляните на следующую программу, чтобы понять реализацию указанного выше метода:
# Using default function as Python decorators def Python_Decorator(funct): # Inner nested function def inner(): print(«This line of code will be printed before the execution of high order function») funct() print(«This line of code will be printed after the execution of high order function») return inner # A default function as decorator def JTP_Decorator(): print(«This line of code will be printed inside the execution of high order function») JTP_Decorator = Python_Decorator(JTP_Decorator) # Python decorator as high order function # Python decorator calling out as high order function JTP_Decorator() This line of code will be printed before the execution of high order function This line of code will be printed inside the execution of high order function This line of code will be printed after the execution of high order function
Источник
Функции и методы в Python: передача функции в функцию. Декоратор | OTUS
Эта статья посвящена теме декораторов в Python. Поговорим о том, что это такое, уделим особое внимание свойствам функций в Python, на базе которых реализована данная идея, а также рассмотрим декораторы, которые принимают аргументы и возвращают значение из функции.
Что надо знать о методах и функциях в Python?
Говоря о функциях в Python, нужно упомянуть два аспекта:
1) функция в Python — есть объект специального вида, который можно передавать в виде аргумента другим функциям;
2) внутри функций в Python вы можете создавать другие функции, а также вызывать их, возвращая результат посредством return.
Теперь давайте поговорим об этом подробнее.
Функция как объект в Python
В языке программирования Python часто практикуется передача одной функции в виде аргумента другой функции. Представьте, что есть список целых чисел, и вы желаете на его базе получить другой список с элементами, которые будут квадратами первого списка. Вот, как это можно реализовать в Python:
>>> # исходный список
>>> a = [1, 2, 3, 4, 5]
>>> # функция, которая возводит в квадрат переданное ей число
>>> sq = lambda x: x**2
>>> # проверка работы функции в Python
>>> print(sq(5))
25
>>> # получение списка квадратов
>>> b = list(map(sq, a))
>>> print(b)
[1, 4, 9, 16, 25]
В нашем примере мы передали функции map в виде первого аргумента функцию sq. Последняя будет по очереди применяться ко всем элементам нашего списка a.
Кроме того, в Python функция является специальным объектом, имеющим метод __call__(). Представьте, что мы создали следующий класс:
class DemoCall():
def __call__(self):
return «Hello!»
Объект такого класса в Python мы сможем вызывать как функцию:
>>> hello = DemoCall()
>>> hello()
'Hello!'
Функция внутри функции в Python
Функции в Python мы можем создавать, вызывать и возвращать из других функций. Кстати, на этом основана идея замыкания (closures) в Python.
Давайте создадим функцию, умножающую 2 числа:
def mul(a):
def helper(b):
return a * b
return helper
В этой функции в Python реализованы два важных свойства:
1) внутри функции mul() мы создаём ещё одну функцию helper();
2) функция mul() возвращает нам функцию helper() в качестве результата работы.
Вызов этой функции в Python:
Особенность заключается в том, что мы можем создавать на базе функции mul() собственные кастомизированные функции. Давайте создадим функцию в Python, умножающую на 3:
>>>three_mul = mul(3)
>>>three_mul(5)
15
В результате была построена функция three_mul(), умножающая на 3 любое переданное ей число.
Декоратор функции в Python
Конструктивно речь идёт о некоторой функции, в качестве аргумента которого выступает другая функция. Декоратор в Python добавляет дополнительный функционал к функции, не меняя её содержимое.
Создание
Представьте, что мы имеем пару простых функций в Python:
def first_test():
print(«Test function 1»)
def second_test():
print(«Test function 2»)
При этом мы желаем их дополнить таким образом, чтобы перед вызовом основного кода нашей функции печаталась строчка “Start function”, а в конце – строка “Stop function”.
Реализовать поставленную задачу можно несколькими методами. Во-первых, мы можем добавить необходимые строки в конец и начало каждой функции. Но вряд ли это удобно, ведь если мы пожелаем их убрать, придётся модифицировать тело функции.
Теперь поговорим о втором пути. Для начала создадим функцию:
def simple_decore(fn):
def wrapper():
print(«Start function»)
fn()
print(«Stop function»)
return wrapper
Теперь нужно обернуть функции в оболочку:
first_test_wrapped = simple_decore(first_test)
second_test_wrapped = simple_decore(second_test)
Обратите внимание, что функции first_test и second_test не поменялись.
>>> first_test()
Test function 1
>>> second_test()
Test function 2
Наши функции second_test_wrapped и first_test_wrapped обладают функционалом, который нам и нужен.
>>> first_test_wrapped()
Start function
Test function 1
Stop function
>>> first_test_wrapped()
Start function
Test function 1
Stop function
Теперь, если надо, чтобы так работали функции с именами first_test и second_test, делаем следующее:
first_test = first_test_wrapped
second_test = second_test_wrapped
Проверяем:
>>> first_test()
Start function
Test function 1
Stop function
>>> second_test()
Start function
Test function 2
Stop function
Выполненные нами действия и являются реализацией идеи декоратора.
Правда, вместо строк:
def first_test():
print(«Test function 1»)
first_test_wrapped = simple_decore(first_test)
first_test = first_test_wrapped
мы можем написать иначе:
@simple_decore
def first_test():
print(«Test function 1»)
В нашем случае @simple_decore – это не что иное, как декоратор функции.
Передаём аргументы в функцию с помощью декоратора
Бывает, что функция требует наличие аргумента, поэтому мы можем передать его через декоратор. Давайте создадим декоратор, принимающий аргумент и выводящий информацию о декорируемой нами функции и её аргументе.
def param_transfer(fn):
def wrapper(arg):
print(«Start function: » + str(fn.__name__) + «(), with param: » + str(arg))
fn(arg)
return wrapper
Чтобы продемонстрировать работу, создадим функцию, выводящую квадратный корень переданного ей числа, а в качестве декоратора, укажем созданный param_transfer:
@param_transfer
def print_sqrt(num):
print(num**0.5)
Теперь давайте выполним данную функцию с аргументом 4:
>>> print_sqrt(4)
Start function: print_sqrt(), with param: 4
2.0
Декораторы для методов класса в Python
С декоратором можно объявлять и методы классов. Давайте выполним модификацию декоратора param_transfer:
def method_decor(fn):
def wrapper(self):
print(«Start method: » + str(fn.__name__))
fn(self)
return wrapper
Теперь приступим к созданию класса для представления 2-мерного вектора (математического). В классе определим метод norm(), выводящий модуль вектора.
class Vector():
def __init__(self, px = 0, py = 0):
self.px = px
self.py = py @method_decor
def norm(self):
print((self.px**2 + self.py**2)**0.5)
Что же, осталось продемонстрировать работу нашего метода:
>>> vc = Vector(px=10, py=5)
>>> vc.norm()
Start method: norm
11.180339887498949
Возвращаем результат работы функции через декоратор
Зачастую создаваемые функции выполняют возвращение какого-либо значения. Чтобы это стало возможным осуществить через декоратор, нужно специальным образом построить нашу внутреннюю функцию:
def decor_with_return(fn):
def wrapper(*args, **kwargs):
print(«Start method: » + str(fn.__name__))
return fn(*args, **kwargs)
return wrapper
Возможно применение этого декоратора и для оборачивания функций, принимающих различные аргументы и возвращающие значение:
@decor_with_return
def calc_sqrt(val):
return val**0.5
Осталось выполнить функцию calc_sqrt() с параметром 16:
>>> tmp = calc_sqrt(16)
Start method: calc_sqrt
>>> print(tmp)
Изучаем продвинутые возможности Python, часть 2: замыкания, декораторы, модуль functools
В первой части серии публикаций о продвинутых возможностях Python мы познакомились с итераторами, генераторами и модулем itertools. В сегодняшней публикации речь пойдёт о замыканиях, декораторах и модуле functools.
Декораторы
Декоратор — паттерн проектирования, при использовании которого класс или функция изменяет или дополняет функциональность другого класса или функции без использования наследования или прямого изменения исходного кода.
В Python декораторы представляют собой функции или любые вызываемые объекты, которые принимают на вход набор необязательных аргументов и функцию или класс и возвращают функцию или класс.
Их можно использовать для реализации паттерна проектирования декоратора или для решения других задач. Декораторы классов появились в Python 2.6.
Кстати, если вы не знакомы с замыканиями Python, прежде чем читать дальше ознакомьтесь с дополнением о замыканиях в конце этой статьи. Концепцию декораторов сложно понять, если вы не знакомы с замыканиями.
https://www.youtube.com/watch?v=Va-ovLxHmus\u0026pp=ygVi0KTRg9C90LrRhtC40Lgg0LLRi9GB0YjQtdCz0L4g0L_QvtGA0Y_QtNC60LAg0LIgUHl0aG9uOiDQt9Cw0LzRi9C60LDQvdC40Y8g0Lgg0LTQtdC60L7RgNCw0YLQvtGA0Ys%3D
В Python декораторы применяются к функции или классу с помощью символа @. В качестве первого примера давайте используем простой декоратор, который регистрирует вызовы функций.
В этом примере декоратор принимает формат времени в качестве аргумента и печатает лог перед и после выполнения декорированной функции с временем исполнения.
Это может быть кстати, когда вы сравниваете эффективность разных реализаций алгоритма или разных алгоритмов.
def logged(time_format): def decorator(func): def decorated_func(*args, **kwargs): print(«- Running '{}' on {} «.format( func.__name__, time.strftime(time_format) )) start_time = time.time() result = func(*args, **kwargs) end_time = time.time() print(«- Finished '{}', execution time = {:0.3f}s «.format( func.__name__, end_time — start_time )) return result decorated_func.__name__ = func.__name__ return decorated_func return decorator
Посмотрите на пример использования. Здесь функции add1 и add2 оформлены с помощью logged, а также дан пример вывода. Заметьте, что формат времени хранится в замыкании возвращаемых функций с декоратором. Поэтому понимание замыканий необходимо для понимания декораторов Python.
Также обратите внимание, как имя возвращаемой функции заменяется именем оригинальной функции в случае, если оно используется позже. Python не делает этого по умолчанию.
@logged(«%b %d %Y — %H:%M:%S»)
def add1(x, y): time.sleep(1) return x + y @logged(«%b %d %Y — %H:%M:%S»)
def add2(x, y): time.sleep(2) return x + y print(add1(1, 2))
print(add2(1, 2)) # Output:
— Running 'add1' on Jul 24 2013 — 13:40:47
— Finished 'add1', execution time = 1.001s
3
— Running 'add2' on Jul 24 2013 — 13:40:48
— Finished 'add2', execution time = 2.001s
3
Если вы достаточно внимательны, то заметите, что мы заботимся, чтобы у возвращаемой функции был правильно указан __name__, но не заботимся о __doc__ или __module__. Поэтому если у функции add есть строка документации, она потеряется.
Как можно этого избежать? Мы могли бы справиться с проблемой так же, как при обработке __name__. Но выполнять такие операции с каждым декоратором утомительно. Поэтому в модуле functools есть декоратор wraps, который срабатывает именно в таком сценарии.
Использование декоратора внутри другого декоратора может показаться странным. Но если вы думаете о декораторах как о функциях, которые принимают функции в качестве параметров и возвращают функции, всё становится на места.
Декоратор wraps используется в следующих примерах вместо ручной обработки __name__ и других подобных атрибутов.
Следующий пример немного сложнее. Давайте напишем декоратор, который кэширует результат вызова функции в течение указанного в секундах времени.
Код ожидает, что переданные в функцию аргументы — хэшируемые объекты (hashable objects), потому что мы используем кортеж с аргументами args в качестве первого параметра и замороженный набор элементов в kwargs в качестве второго параметра, который выступает ключом кэша. У каждой функции будет уникальный кэш dict, который хранится в замыкании функции.
import time
from functools import wraps def cached(timeout, logged=False): «»»Decorator to cache the result of a function call. Cache expires after timeout seconds. «»» def decorator(func): if logged: print(«— Initializing cache for», func.__name__) cache = {} @wraps(func) def decorated_function(*args, **kwargs): if logged: print(«— Called function», func.__name__) key = args, frozenset(kwargs.items()) result = None if key in cache: if logged: print(«— Cache hit for», func.__name__, key) cache_hit, expiry = cache[key] if time.time() — expiry >> @cached(10, True)
… def fib(n):
… «»»Returns the n'th Fibonacci number.»»»
… if n == 0 or n == 1:
… return 1
… return fib(n — 1) + fib(n — 2)
…
— Initializing cache for fib
>>> dump_closure(fib)
1. Dumping function closure for fib:
— cell 0 = {}
— cell 1 =
— cell 2 = True
— cell 3 = 10
>>>
>>> print(«Testing — F(4) = {}».format(fib(4)))
— Called function fib
— Cache miss for fib ((4,), frozenset([]))
— Called function fib
— Cache miss for fib ((3,), frozenset([]))
— Called function fib
— Cache miss for fib ((2,), frozenset([]))
— Called function fib
— Cache miss for fib ((1,), frozenset([]))
— Called function fib
— Cache miss for fib ((0,), frozenset([]))
— Called function fib
— Cache hit for fib ((1,), frozenset([]))
— Called function fib
— Cache hit for fib ((2,), frozenset([]))
Testing — F(4) = 5
Декораторы класса
В предыдущем разделе мы рассмотрели декораторы функций и некоторые необычные способы их применения. Теперь давайте рассмотрим декораторы классов. В данном случае декоратор принимает на вход класс (объект с типом type в Python) и возвращает модифицированный класс.
Первый пример — простая математика. Дано частично упорядоченное множество P. Мы определяем Pd как дуальность P, исключительно если P(x,y)⟺Pd(y,x). Другими словами, речь идёт об обратном порядке.
Как можно реализовать это с помощью Python? Предположим, класс определяет порядок с помощью методов __lt__, __le__ и так далее.
Тогда мы можем написать декоратор класса, который заменяет каждую функцию её дуальностью.
def make_dual(relation): @wraps(relation, ['__name__', '__doc__']) def dual(x, y): return relation(y, x) return dual def dual_ordering(cls): «»»Class decorator that reverses all the orderings»»» for func in ['__lt__', '__gt__', '__ge__', '__le__']: if hasattr(cls, func): setattr(cls, func, make_dual(getattr(cls, func))) return cls
Вот как это можно применить к str, чтобы создать новый класс rstr, в котором используется обратный лексикографический порядок.
@dual_ordering
class rstr(str): pass x = rstr(«1»)
y = rstr(«2») print x = y # Output:
False
False
True
True
Давайте посмотрим на более сложный пример. Предположим, мы хотим применить декоратор logged из предыдущего примера ко всем методам в классе. Это можно сделать вручную: просто добавить декоратор в каждый метод.
Также можно автоматизировать процесс с помощью декоратора класса. Прежде чем сделать это, автор улучшил декоратор logged из предыдущего раздела. Теперь в нём используется атрибут wraps из модуля functools вместо ручной работы с __name__.
Также здесь в возвращаемую функцию добавлен атрибут _logged_decorator. Его значение True, он применяется, чтобы избежать двойного декорирования функции. Это удобно, когда мы применяем декоратор к классам, которые должны наследовать методы от других классов.
Наконец, добавлен аргумент name_prefix, который делает возможной кастомизацию сообщений лога.
def logged(time_format, name_prefix=»»): def decorator(func): if hasattr(func, '_logged_decorator') and func._logged_decorator: return func @wraps(func) def decorated_func(*args, **kwargs): start_time = time.time() print(«- Running '{}' on {} «.format( name_prefix + func.__name__, time.strftime(time_format) )) result = func(*args, **kwargs) end_time = time.time() print(«- Finished '{}', execution time = {:0.3f}s «.format( name_prefix + func.__name__, end_time — start_time )) return result decorated_func._logged_decorator = True return decorated_func return decorator
Теперь можно написать декоратор класса.
def log_method_calls(time_format): def decorator(cls): for o in dir(cls): if o.startswith('__'): continue a = getattr(cls, o) if hasattr(a, '__call__'): decorated_a = logged(time_format, cls.__name__ + «.»)(a) setattr(cls, o, decorated_a) return cls return decorator
Вот как он будет использоваться. Обратите внимание, как здесь обрабатываются переопределённые методы и наследование.
@log_method_calls(«%b %d %Y — %H:%M:%S»)
class A(object): def test1(self): print(«test1») @log_method_calls(«%b %d %Y — %H:%M:%S»)
class B(A): def test1(self): super().test1() print(«child test1») def test2(self): print(«test2») b = B()
b.test1()
b.test2() # Output:
— Running 'B.test1' on Jul 24 2013 — 14:15:03
— Running 'A.test1' on Jul 24 2013 — 14:15:03
test1
— Finished 'A.test1', execution time = 0.000s
child test1
— Finished 'B.test1', execution time = 1.001s
— Running 'B.test2' on Jul 24 2013 — 14:15:04
test2
— Finished 'B.test2', execution time = 2.001s
Наш первый пример декораторов класса должен был изменять порядок методов класса. Похожий декоратор, но более полезный, может принимать один из __lt__, __le__, __gt__ или __ge__ и __eq__, и реализовывать остальные для полного упорядочивания класса. Это именно то, что делает декоратор functools.total_ordering. Подробности .
Несколько примеров из Flask
Рассмотрим несколько интересных примеров использования декораторов в .
Представьте, что хотите, чтобы некоторые функции выводили предупреждающие сообщения, если они вызываются при определённых обстоятельствах в режиме отладки. Вместо того, чтобы вручную добавлять код в начало каждой функции, можно использовать декоратор. Это то, что делает декоратор, который можно найти в файле app.py Flask.
def setupmethod(f): «»»Wraps a method so that it performs a check in debug mode if the first request was already handled. «»» def wrapper_func(self, *args, **kwargs): if self.debug and self._got_first_request: raise AssertionError('A setup function was called after the ' 'first request was handled. This usually indicates a bug ' 'in the application where a module was not imported ' 'and decorators or other functionality was called too late.
' 'To fix this make sure to import all your view modules, ' 'database models and everything related at a central place ' 'before the application starts serving requests.') return f(self, *args, **kwargs) return update_wrapper(wrapper_func, f)
Более интересный пример — декоратор Flask route, который определяется в классе Flask. Заметьте, что декоратор может быть методом класса. В этом случае в качестве первого параметра используется self. Полный код смотрите в файле app.py. Обратите внимание, декоратор просто регистрирует декорированную функцию как обработчик URL с помощью вызова функции add_url_rule.
def route(self, rule, **options): «»»A decorator that is used to register a view function for a given URL rule. This does the same thing as :meth:`add_url_rule` but is intended for decorator usage:: @app.route('/') def index(): return 'Hello World' For more information refer to :ref:`url-route-registrations`. :param rule: the URL rule as string :param endpoint: the endpoint for the registered URL rule. Flask itself assumes the name of the view function as endpoint :param options: the options to be forwarded to the underlying :class:`~werkzeug.routing.Rule` object. A change to Werkzeug is handling of method options. methods is a list of methods this rule should be limited to (`GET`, `POST` etc.). By default a rule just listens for `GET` (and implicitly `HEAD`). Starting with Flask 0.6, `OPTIONS` is implicitly added and handled by the standard request handling. «»» def decorator(f): endpoint = options.pop('endpoint', None) self.add_url_rule(rule, endpoint, f, **options) return f return decorator
Дополнительное чтение
Много информации о декораторах вы найдёте на официальной Python. Также можно посмотреть замечательное видео Дэвида Безли .
Приложение: замыкания
Замыкание — это комбинация функции и множества ссылок на переменные в области видимости функции. Последнее иногда называют ссылочной средой. Замыкание позволяет выполнять функцию за пределами области видимости. В Python ссылочная среда хранится в виде набора ячеек. Доступ к ним можно получить с помощью атрибутов func_closure или __closure__. В Python 3 используется только __closure__.
Важно понимать, что речь идёт просто о ссылках, а не о глубоких копиях объектов. Конечно, неважно, являются ли объекты неизменяемыми, но для изменяемых объектов, например, списков, это важно. Это иллюстрирует пример ниже. Обратите внимание, у функций также есть __globals__, где хранится глобальное ссылочное окружение, для которого была определена функция. Посмотрите на простой пример:
>>> def return_func_that_prints_s(s):
… def f():
… print(s)
… return f
…
>>> g = return_func_that_prints_s(«Hello»)
>>> h = return_func_that_prints_s(«World»)
>>> g()
Hello
>>> h()
World
>>> g is h
False
>>> h.__closure__
(,)
>>> print([str(c.cell_contents) for c in g.__closure__])
['Hello']
>>> print([str(c.cell_contents) for c in h.__closure__])
['World']
Ещё один пример, более сложный. Убедитесь, что понимаете, почему код работает именно так.
>>> def return_func_that_prints_list(z):
… def f():
… print(z)
… return f
…
>>> z = [1, 2]
>>> g = return_func_that_prints_list(z)
>>> g()
[1, 2]
>>> z.append(3)
>>> g()
[1, 2, 3]
>>> z = [1]
>>> g()
[1, 2, 3]
Наконец, вот пример метода dump_closure, который использовался выше.
def dump_closure(f): if hasattr(f, «__closure__») and f.__closure__ is not None: print(«- Dumping function closure for {}:».format(f.__name__)) for i, c in enumerate(f.__closure__): print(«— cell {} = {}».format(i, c.cell_contents)) else: print(» — {} has no closure!».format(f.__name__))
Адаптированный перевод статьи . Мнение автора оригинальной публикации может не совпадать с мнением администрации «Хекслета».