Обяснени декоратори на Python: От @property до повторна логика за 15 минути (2026)
Декораторите са характеристиката, която разделя „познава Python“ от „чете Python свободно“. Изглеждат магически, но са механични: @decorator на функция е точно f = decorator(f). Това е. Всичко останало в този урок е една линия, приложена в различни форми.
Теорията на един ред
Когато пишете:
Python го трансформира в:
Това е цялата механика. @decorator_name над def изпълнява def_name = decorator_name(def_name) след дефиницията. Ако усвоите това, декораторите спират да бъдат мистериозни завинаги.
Писане на вашия първи декоратор (60 секунди)
Декораторът е всеки извикваем, който приема функция и връща функция. Типична форма:
*args, **kwargs в обвивката е защитен модел — той позволява на декоратора да работи върху всяка функция, независимо от подписа. Пропуснете го и ще получите „TypeError: wrapper() приема 0 позиционни аргумента, но са дадени 2“ в момента, в който някой украси функция с параметри.
Поправката на @functools.wraps
Един фин бъг: след декориране, add.__name__ е "wrapper", а не "add". Документните низове и подсказките за тип също изчезват. Корекцията е едно импортиране:
Правило: всеки декоратор, който пишете, трябва да използва `@functools.wraps(fn)`. Той струва един ред и предотвратява цяла категория болка при отстраняване на грешки (самоанализ, pytest приспособления, logging формати, pickle — всички прекъсват безшумно без него).
Четирите декоратора, които ще видите във всяка кодова база
1. @property — изчислени атрибути
@property прави метод извикан сякаш като атрибут. Перфектен за извлечени стойности само за четене. Има и @area.setter за пътя за запис, но 90% от използванията на @property са само за четене.
2. @staticmethod и @classmethod
@classmethod получава cls (самия клас) и се използва за алтернативни конструктори. @staticmethod не получава нищо специално и е обикновена функция, която просто живее в пространството на имената на класа.
3. @functools.cache — мемоизация
Един от най-мощните декоратори на stdlib. Автоматично запаметява функцията въз основа на нейните аргументи. Изисква всички аргументи да могат да се хешират (без списъци / dicts като аргументи — използвайте кортежи).
4. @dataclasses.dataclass — елиминиране на шаблон
@dataclass чете анотациите за тип на ниво клас и автоматично генерира __init__, __repr__, __eq__ и (с frozen=True) __hash__. Това е най-голямото подобрение на качеството на живот в Python 3.7+.
Писане на декоратор @retry (пример от реалния свят)
Декоратор, който опитва отново функция при повреда с експоненциално забавяне:
Обърнете внимание на трите вложени функции: retry приема конфигурационните аргументи и връща decorator; decorator приема функцията и връща wrapper; wrapper изпълнява цикъла за повторен опит. Този трислоен модел е това, което всеки декоратор-с-аргументи изглежда - запомнете формата, а не конкретния пример.
Базирани на клас декоратори (рядко срещани, но полезни)
Декоратор е всеки, който може да бъде призован. Класове с __call__ също работят:
Използвайте, когато декораторът трябва да задържи състояние, което надживява едно извикване (брояч, кеш, кофа за ограничаване на скоростта).
Често срещани клопки
1. Забравяне на `@functools.wraps` — интроспекцията прекъсва безшумно.
2. Променливи аргументи по подразбиране в самия декоратор — класическият def wrapper(x, cache=[]) бъгове и тук.
3. Декориране на методи без приемане на `self` — не забравяйте, че self е просто позиционен аргумент; *args, **kwargs го хваща добре.
4. Подреждането има значение при подреждане: @a\n@b\ndef f() е f = a(b(f)). Четете стекове отдолу нагоре.
Практикувайте
Напишете декоратор „@timing“, който отпечатва колко време отнема изпълнението на обвитата функция, след което го приложете към функция, която изчислява „sum(range(10_000_000))“. Целият декоратор е 8 реда с functools.wraps + time.perf_counter.
След като разберете, че @x е f = x(f), декораторите стават една от най-продуктивните функции на Python. Те са навсякъде в реални кодови бази: Flask/FastAPI маршрути (@app.route), pytest fixtures (@pytest.fixture), Django изгледи (@login_required), Pydantic валидатори (@field_validator). Научаването да ги пишете сами отключва способността да четете всеки един от тях.
Следваща стъпка: 10 практически урока по декориране — изградете @retry, @memoise, @rate_limit и @requires_auth от нулата с истински Python, работещ във вашия браузър.