Python Academy · Урок

Написание пользовательских декораторов

Создавайте декораторы с помощью функций-обёрток и functools.wraps.

Урок 2 из 414 шагов

«Написание пользовательских декораторов» — бесплатный урок Python Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Python Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Python Academy содержит 4 уроков всего.

Введение

Декораторы — это функции, которые оборачивают другие функции и добавляют поведение до или после их выполнения, не изменяя исходную функцию.

Что такое декоратор

@decorator над функцией — это синтаксический сахар для записи func = decorator(func). Декоратор получает функцию и возвращает обёртку.
def shout(func):
    def wrapper(*a, **kw):
        result = func(*a, **kw)
        return str(result).upper()
    return wrapper
@shout
def greet(name): return f'hello {name}'
print(greet('Alice'))

Шаблон обёртки

Функция-обёртка имеет ту же сигнатуру, что и исходная функция. Она вызывает исходную функцию внутри себя, добавляя поведение до и после вызова.
def timer(func):
    import time
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        print(f'{func.__name__} took {time.time()-start:.4f}s')
        return result
    return wrapper
@timer
def slow(): import time; time.sleep(0.01)
slow()

functools.wraps

@wraps(func), применённый к обёртке, сохраняет __name__, __doc__ и __annotations__ исходной функции.
from functools import wraps
def log(func):
    @wraps(func)
    def wrapper(*a, **kw):
        print(f'calling {func.__name__}')
        return func(*a, **kw)
    return wrapper
@log
def add(a, b): return a + b
print(add.__name__)
add(1, 2)

Декоратор с аргументами

Декоратору с аргументами нужен ещё один уровень обёртки: фабричная функция, которая принимает аргументы и возвращает декоратор.
def repeat(n):
    def decorator(func):
        from functools import wraps
        @wraps(func)
        def wrapper(*a, **kw):
            for _ in range(n): func(*a, **kw)
        return wrapper
    return decorator
@repeat(3)
def hi(): print('hi')
hi()

Комбинирование декораторов

Несколько декораторов применяются снизу вверх: @d2 @d1 означает func = d2(d1(func)). Самый внешний декоратор, расположенный сверху, выполняется последним.
def bold(f):
    from functools import wraps
    @wraps(f)
    def w(*a,**k): return '<b>' + f(*a,**k) + '</b>'
    return w
def italic(f):
    from functools import wraps
    @wraps(f)
    def w(*a,**k): return '<i>' + f(*a,**k) + '</i>'
    return w
@bold
@italic
def text(): return 'hello'
print(text())

Декоратор на основе класса

Класс с __call__ может быть декоратором. Он сохраняет состояние между вызовами.
class Counter:
    def __init__(self, func): self.func,self.count=func,0
    def __call__(self, *a, **kw):
        self.count+=1
        return self.func(*a, **kw)
@Counter
def greet(name): return f'hi {name}'
greet('Alice'); greet('Bob')
print(greet.count)

Декоратор мемоизации

Кэшируйте предыдущие результаты, чтобы не выполнять вычисления повторно: if args in cache: return cache[args]. functools.lru_cache делает это встроенным образом.
from functools import lru_cache
@lru_cache(maxsize=None)
def fib(n):
    if n < 2: return n
    return fib(n-1) + fib(n-2)
print(fib(30))

Декоратор авторизации

Декораторы часто используют для контроля доступа: перед выполнением функции проверьте, вошёл ли пользователь в систему.
def requires_auth(func):
    from functools import wraps
    @wraps(func)
    def wrapper(user, *a, **kw):
        if not user.get('authenticated'):
            raise PermissionError('Not authenticated')
        return func(user, *a, **kw)
    return wrapper
@requires_auth
def dashboard(user): return 'Welcome!'
try: dashboard({'authenticated': False})
except PermissionError as e: print(e)

Декоратор повторных попыток

Оберните функцию, чтобы повторять вызов при ошибке: for _ in range(retries): try: return func() except Exception: pass
import random
from functools import wraps
def retry(times=3):
    def dec(func):
        @wraps(func)
        def wrapper(*a, **kw):
            for i in range(times):
                try: return func(*a, **kw)
                except Exception as e:
                    if i==times-1: raise
        return wrapper
    return dec
@retry(3)
def flaky():
    if random.random()<0.7: raise ValueError
    return 'ok'
try: print(flaky())
except ValueError: print('failed')

Рекомендации по декораторам

Всегда используйте @functools.wraps. Каждый декоратор должен решать одну задачу. Документируйте, что именно он добавляет. Тестируйте декоратор отдельно от декорированной функции.
from functools import wraps
def validate_positive(func):
    @wraps(func)
    def wrapper(x, *a, **kw):
        if x <= 0: raise ValueError(f'{x} must be positive')
        return func(x, *a, **kw)
    return wrapper
@validate_positive
def sqrt(x): return x**0.5
print(sqrt(9))

Быстрая проверка

Что делает @functools.wraps(func), применённый к обёртке?

Итоги

Декораторы: func = decorator(func). Используйте wrapper(*args, **kwargs). @wraps сохраняет метаданные. Параметризованные декораторы добавляют ещё один уровень. Несколько декораторов объединяются с помощью @decorators.

Продолжайте

Отличный прогресс! Продолжайте, чтобы освоить следующую концепцию.
Можно начать бесплатно

Изучай Python с ИИ-репетитором — бесплатно

Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.

Курсы
76
Уроки
320

Часто задаваемые вопросы

Урок «Написание пользовательских декораторов» бесплатный?

Да — полный текст урока «Написание пользовательских декораторов» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Python Academy, подпишись на CoddyKit PRO. Курс Python Academy содержит 4 уроков всего.

Чему я научусь в уроке «Написание пользовательских декораторов»?

Создавайте декораторы с помощью функций-обёрток и functools.wraps. Ты практикуешь Python Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Python Academy?

Предыдущий опыт не требуется. Python Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «Написание пользовательских декораторов»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Python Academy?

Да. Каждый урок Python Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Функции как объекты первого класса
  2. Написание пользовательских декораторов
  3. Декоратор @property
  4. @classmethod и @staticmethod
← Назад к Python Academy