Python Glossary English

все 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Сообщить о неточности