Декораторы Python
Декоратор Python предоставляет лаконичный и многократно используемый способ расширения функции или класса. Прочтите сопутствующую статью Декораторы Python: Простые шаблоны для улучшения вашего кода для практических примеров и шаблонов.
Базовый декоратор
Декоратор в своей простейшей форме — это функция, которая принимает другую функцию в качестве аргумента и возвращает обертку (wrapper). Следующий пример показывает создание декоратора и его использование.
# Декоратор: функция, которая принимает другую функцию и возвращает обертку
def your_decorator(func):
def wrapper():
# Сделать что-то до вызова func...
print("Before func!")
func() # Вызвать исходную функцию
# Сделать что-то после вызова func...
print("After func!")
return wrapper # Вернуть функцию-обертку
# @your_decorator — это синтаксический сахар для: foo = your_decorator(foo)
@your_decorator
def foo():
print("Hello World!")
foo() # Вызывает wrapper, который вызывает foo с дополнительным поведением
Before func!
Hello World!
After func!
Войдите в систему, чтобы ответить на эту викторину и отслеживать свой прогресс обучения
Декоратор для функции с параметрами
# Декоратор, который работает с функциями, имеющими параметры
def your_decorator(func):
def wrapper(*args,**kwargs): # Принимает любые аргументы
# Сделать что-то до вызова func...
print("Before func!")
func(*args,**kwargs) # Передать аргументы исходной функции
# Сделать что-то после вызова func...
print("After func!")
return wrapper
@your_decorator
def foo(bar):
print("My name is " + bar)
foo("Jack") # Аргументы передаются через wrapper
Before func!
My name is Jack
After func!
Шаблон для базового декоратора
Этот шаблон полезен для большинства вариантов использования декораторов. Он применим к функциям с параметрами или без них, а также с возвращаемым значением или без него.
import functools
# Шаблон декоратора с лучшими практиками: сохраняет метаданные функции и возвращаемое значение
def your_decorator(func):
@functools.wraps(func) # Сохраняет имя функции, docstring и т. д.
def wrapper(*args,**kwargs):
# Сделать что-то до вызова func...
result = func(*args,**kwargs) # Вызвать функцию и захватить возвращаемое значение
# Сделать что-то после вызова func..
return result # Вернуть возвращаемое значение исходной функции
return wrapper
Войдите в систему, чтобы ответить на эту викторину и отслеживать свой прогресс обучения
@functools.wraps(func) в декораторе?Декоратор с параметрами
Вы также можете определить параметры для использования декоратором.
import functools
# Фабрика декораторов: возвращает декоратор на основе параметров
def your_decorator(arg):
def decorator(func):
@functools.wraps(func) # Сохранить метаданные функции
def wrapper(*args,**kwargs):
# Сделать что-то до вызова func, возможно, используя arg...
result = func(*args,**kwargs)
# Сделать что-то после вызова func, возможно, используя arg...
return result
return wrapper
return decorator # Вернуть фактическую функцию-декоратор
Чтобы использовать этот декоратор:
# Использование декоратора с параметрами: @your_decorator(arg='x') вызывает your_decorator('x')
# который возвращает декоратор, который затем применяется к foo
@your_decorator(arg = 'x')
def foo(bar):
return bar
Декораторы на основе классов
Чтобы декорировать метод класса, вы должны определить декоратор внутри класса. Когда методу передается только неявный аргумент self, без каких-либо явных дополнительных аргументов, вы должны создать отдельный декоратор только для этих методов без каких-либо дополнительных аргументов. Пример этого показан ниже, когда вы хотите перехватывать и выводить исключения определенным образом.
# Декоратор метода класса: определяется внутри класса
class DecorateMyMethod:
# Декоратор статического метода для методов, имеющих только параметр 'self'
def decorator_for_class_method_with_no_args(method):
def wrapper_for_class_method(self): # Принимает только self
try:
return method(self) # Вызвать исходный метод
except Exception as e:
print("\nWARNING: Please make note of the following:\n")
print(e)
return wrapper_for_class_method
def __init__(self,succeed:bool):
self.succeed = succeed
@decorator_for_class_method_with_no_args
def class_action(self):
if self.succeed:
print("You succeeded by choice.")
else:
raise Exception("Epic fail of your own creation.")
test_succeed = DecorateMyMethod(True)
test_succeed.class_action()
You succeeded by choice.
test_fail = DecorateMyMethod(False)
test_fail.class_action()
Exception: Epic fail of your own creation.
Декоратор также может быть определен как класс вместо метода. Это полезно для поддержания и обновления состояния, как в следующем примере, где мы подсчитываем количество вызовов метода:
# Декоратор на основе класса: поддерживает состояние между вызовами
class CountCallNumber:
def __init__(self, func):
self.func = func # Сохранить декорируемую функцию
self.call_number = 0 # Инициализировать счетчик вызовов
def __call__(self, *args, **kwargs): # Делает экземпляр вызываемым
self.call_number += 1 # Увеличить счетчик
print("This is execution number " + str(self.call_number))
return self.func(*args, **kwargs) # Вызвать исходную функцию
@CountCallNumber # Создает экземпляр CountCallNumber
def say_hi(name):
print("Hi! My name is " + name)
say_hi("Jack") # Вызывает CountCallNumber.__call__()
This is execution number 1
Hi! My name is Jack
say_hi("James")
This is execution number 2
Hi! My name is James
Пример подсчета
Этот пример подсчета вдохновлен видеоуроком Патрика Лобера на YouTube.