все Python 3Функциипонятие
Декораторы
декораторы
Функция высшего порядка, оборачивающая другую функцию для изменения её поведения. Используется @синтаксис.
Синтаксис
@decorator
def func(): ...
Запись @deco над def f — это ровно f = deco(f), она выполняется один раз в момент определения функции. Главная грабля: без functools.wraps обёртка подменяет собой оригинал, и f.__name__, docstring и сигнатура теряются — help() и трейсбеки начинают врать; поэтому wrapper оборачивают в @wraps(func).
Примеры
def my_dec(func):
def wrapper(*args, **kwargs):
print('before')
result = func(*args, **kwargs)
print('after')
return result
return wrapper
@my_dec
def say(): print('hi')
say() # → before / hi / after
import time
def timer(func):
def wrapper(*args, **kwargs):
t = time.perf_counter()
res = func(*args, **kwargs)
print(f'{func.__name__}: {time.perf_counter() - t:.2f}s')
return res
return wrapper
@timer
def slow(): time.sleep(0.01)
slow() # → ? около slow: 0.01s — зависит от машины
from functools import wraps
def logged(func):
@wraps(func)
def wrapper(*a, **kw):
print(f'Calling {func.__name__}')
return func(*a, **kw)
return wrapper
@logged
def add(a, b): return a + b
print(add(1, 2)) # → Calling add / 3
# Декоратор с аргументами
def repeat(n):
def decorator(func):
def wrapper(*a, **kw):
for _ in range(n):
func(*a, **kw)
return wrapper
return decorator
@repeat(3)
def hi(): print('hi')
hi() # → hi / hi / hi
# Стек декораторов: применяются снизу вверх
@my_dec
@logged
def greet(name): return f'Hi {name}'
greet('Alice') # → before / Calling greet / after
# Декоратор для проверки типа
def check_int(func):
def wrapper(n):
if not isinstance(n, int):
raise TypeError('int expected')
return func(n)
return wrapper
@check_int
def triple(n): return n * 3
print(triple(4)) # → 12
# Сохранение имени функции (@wraps)
from functools import wraps
def noop(f):
@wraps(f)
def w(*a,**kw): return f(*a,**kw)
return w
@noop
def my_func(): pass
print(my_func.__name__) # → my_func
См. также
Открыть в полном глоссарииdocs.python.orgСообщить о неточности