Перейти до основного вмісту
Проміжний Python2026-07-29 · 10 хв читання

Пояснення декораторів Python: від @property до повторної логіки за 15 хвилин (2026)

Декоратори — це функція, яка відокремлює «знає Python» від «вільно читає Python». Вони виглядають чарівно, але вони механічні: @decorator у функції — це точно f = decorator(f). Ось і все. Все інше в цьому підручнику полягає в тому, що одна лінія застосована в різних формах.

Теорія однієї лінії

Коли ви пишете:

▶ PYTHONeditable · runs in your browser

Python перетворює його на:

▶ PYTHONeditable · runs in your browser

Ось і вся механіка. @decorator_name над def запускає def_name = decorator_name(def_name) після визначення. Якщо ви засвоїте це, декоратори назавжди перестануть бути загадковими.

Написання вашого першого декоратора (60 секунд)

Декоратор — це будь-який виклик, який приймає функцію та повертає її. Типова форма:

▶ PYTHONeditable · runs in your browser

*args, **kwargs в обгортці є захисним шаблоном — він дозволяє декоратору працювати з будь-якою функцією незалежно від підпису. Пропустіть це, і ви отримаєте TypeError: wrapper() приймає 0 позиційних аргументів, але було надано 2 у той момент, коли хтось прикрашає функцію параметрами.

Виправлення @functools.wraps

Одна тонка помилка: після декорування add.__name__ є "wrapper", а не "add"`. Рядки документації та підказки типу також зникають. Виправлення полягає в одному імпорті:

▶ PYTHONeditable · runs in your browser

Правило: кожен декоратор, який ви пишете, має використовувати `@functools.wraps(fn)`. Він коштує один рядок і запобігає цілій категорії проблем з налагодженням (самоаналіз, кріплення pytest, форматувальники logging, pickle — усі без нього ламаються).

Чотири декоратори, які ви побачите в кожній кодовій базі

1. @property — обчислені атрибути

▶ PYTHONeditable · runs in your browser

@property робить метод викликаним як якщо б це був атрибут. Ідеально підходить для похідних значень лише для читання. Існує також @area.setter для шляху запису, але 90% використання @property призначено лише для читання.

2. @staticmethod і @classmethod

▶ PYTHONeditable · runs in your browser

@classmethod отримує cls (самий клас) і використовується для альтернативних конструкторів. @staticmethod не отримує нічого особливого і є простою функцією, яка випадково живе в просторі імен класу.

3. @functools.cache — запам'ятовування

▶ PYTHONeditable · runs in your browser

Один із найпотужніших декораторів stdlib. Автоматично запам’ятовує функцію на основі її аргументів. Вимагає, щоб усі аргументи були хешованими (без списків/диктів як аргументів — використовуйте кортежі).

4. @dataclasses.dataclass — усунення шаблону

▶ PYTHONeditable · runs in your browser

@dataclass читає анотації типу на рівні класу та автоматично генерує __init__, __repr__, __eq__ та (із frozen=True) __hash__. Це найбільше покращення якості життя в Python 3.7+.

Написання декоратора @retry (реальний приклад)

Декоратор, який повторює спробу функції в разі помилки з експоненціальним відстрочкою:

▶ PYTHONeditable · runs in your browser

Зверніть увагу на три вкладені функції: retry приймає параметри конфігурації та повертає decorator; decorator приймає функцію та повертає wrapper; wrapper запускає цикл повторення. Цей тришаровий шаблон виглядає як будь-який декоратор з аргументами — запам’ятовуйте форму, а не конкретний приклад.

Класові декоратори (рідкісні, але корисні)

Декоратор — це будь-яке покликання. Класи з __call__ також працюють:

▶ PYTHONeditable · runs in your browser

Використовуйте, коли декоратору потрібно утримувати стан, який переживає один виклик (лічильник, кеш, відро обмеження швидкості).

Поширені підводні камені

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 (@pytest.fixture), перегляди Django (@login_required), валідатори Pydantic (@field_validator). Якщо навчитися писати їх самостійно, ви зможете прочитати кожен із них.

Наступний крок: 10 практичних уроків декоратора — створіть @retry, @memoise, @rate_limit і @requires_auth з нуля за допомогою справжнього Python, запущеного у вашому браузері.

Готові спробувати?

15 безкоштовних уроків після безкоштовної реєстрації — без картки. Спробуйте платформу та вирішуйте.

Почніть безкоштовно →

Or unlock every lesson

Pro — $12/mo, unlimited AI, cancel any time. Refund within 14 days worldwide.

See Pro plans →

Get one Python lesson + one career idea every Friday

No spam, no "buy our course now". Three bullets, every Friday. Unsubscribe with one click.