<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Полезные декораторы в Python — исчерпывающий гайд]]></title><description><![CDATA[<p dir="auto">Декоратор в Python это обычная функция, которая принимает другую функцию (или класс) как аргумент, расширяет её поведение и возвращает новый вызываемый объект. Синтаксис <code>@decorator</code> это просто синтаксический сахар: запись <code>@timer</code> над функцией <code>foo</code> полностью эквивалентна <code>foo = timer(foo)</code>.</p>
<pre><code class="language-python">def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("до вызова")
        result = func(*args, **kwargs)
        print("после вызова")
        return result
    return wrapper

@my_decorator
def greet(name):
    print(f"Привет, {name}!")

# Эквивалентно: greet = my_decorator(greet)
greet("Alice")
# до вызова
# Привет, Alice!
# после вызова
</code></pre>
<p dir="auto">Механизм работы в три шага: Python видит <code>@decorator</code>, вызывает <code>decorator(func)</code> в момент определения функции (не при вызове), сохраняет результат под тем же именем. Это важно: код декоратора вне <code>wrapper</code> выполняется один раз при загрузке модуля.</p>
<hr />
<h2>functools.wraps — первое правило декораторов</h2>
<p dir="auto">Без <code>@wraps</code> задекорированная функция теряет своё имя, docstring и сигнатуру. Это ломает отладку, <code>help()</code>, <code>inspect</code>, Sphinx-документацию и любые инструменты интроспекции.</p>
<pre><code class="language-python">from functools import wraps

def my_decorator(func):
    @wraps(func)          # копирует __name__, __doc__, __module__, __annotations__
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def add(a: int, b: int) -&gt; int:
    """Складывает два числа."""
    return a + b

print(add.__name__)   # add  (без @wraps было бы 'wrapper')
print(add.__doc__)    # Складывает два числа.
</code></pre>
<p dir="auto"><code>@wraps</code> под капотом вызывает <code>functools.update_wrapper</code>, который копирует атрибуты <code>__module__</code>, <code>__name__</code>, <code>__qualname__</code>, <code>__annotations__</code>, <code>__doc__</code> и обновляет <code>__wrapped__</code>, позволяя при необходимости добраться до оригинальной функции через <code>add.__wrapped__</code>.</p>
<hr />
<h2>Встроенные декораторы — стандартная библиотека</h2>
<h3>@property</h3>
<p dir="auto">Превращает метод в управляемый атрибут с геттером, сеттером и делитером. Позволяет добавить валидацию без изменения публичного API класса.</p>
<pre><code class="language-python">class Temperature:
    def __init__(self, celsius: float = 0):
        self._celsius = celsius

    @property
    def celsius(self) -&gt; float:
        return self._celsius

    @celsius.setter
    def celsius(self, value: float):
        if value &lt; -273.15:
            raise ValueError("Ниже абсолютного нуля невозможно")
        self._celsius = value

    @property
    def fahrenheit(self) -&gt; float:
        return self._celsius * 9 / 5 + 32

t = Temperature(100)
print(t.fahrenheit)   # 212.0
t.celsius = -300      # ValueError
</code></pre>
<h3>@staticmethod и @classmethod</h3>
<p dir="auto"><code>@staticmethod</code> объявляет метод, не привязанный ни к экземпляру (<code>self</code>), ни к классу (<code>cls</code><img src="https://forum.exlends.com/assets/plugins/nodebb-plugin-emoji/emoji/android/1f61e.png?v=a9b928d4b2f" class="not-responsive emoji emoji-android emoji--disappointed" style="height:23px;width:auto;vertical-align:middle" title="):" alt="😞" /> это просто функция внутри пространства имён класса. <code>@classmethod</code> передаёт сам класс первым аргументом, что позволяет создавать альтернативные конструкторы.</p>
<pre><code class="language-python">class Date:
    def __init__(self, year: int, month: int, day: int):
        self.year, self.month, self.day = year, month, day

    @classmethod
    def from_string(cls, s: str) -&gt; "Date":
        year, month, day = map(int, s.split("-"))
        return cls(year, month, day)      # работает и в подклассах

    @staticmethod
    def is_leap_year(year: int) -&gt; bool:
        return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)

d = Date.from_string("2024-03-15")
print(Date.is_leap_year(2024))   # True
</code></pre>
<h3>@dataclass</h3>
<p dir="auto">Автоматически генерирует <code>__init__</code>, <code>__repr__</code>, <code>__eq__</code> и, опционально, методы сравнения и хэширования. Начиная с Python 3.10 можно использовать <code>slots=True</code> для экономии памяти.</p>
<pre><code class="language-python">from dataclasses import dataclass, field

@dataclass(order=True, frozen=True)
class Point:
    x: float
    y: float
    label: str = field(default="", compare=False)

p1 = Point(1.0, 2.0, "A")
p2 = Point(3.0, 4.0, "B")
print(p1 &lt; p2)    # True (сравнивает x, затем y)
# p1.x = 5.0     # FrozenInstanceError, т.к. frozen=True
</code></pre>
<p dir="auto">Параметры <code>eq</code>, <code>order</code>, <code>frozen</code>, <code>slots</code>, <code>kw_only</code> (3.10+) и <code>match_args</code> (3.10+) позволяют точно настроить поведение.</p>
<h3>@abstractmethod</h3>
<p dir="auto">Из модуля <code>abc</code>. Помечает метод как абстрактный: класс, содержащий хотя бы один такой метод, нельзя инстанциировать. Подкласс обязан переопределить все абстрактные методы.</p>
<pre><code class="language-python">from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self) -&gt; float: ...

    @abstractmethod
    def perimeter(self) -&gt; float: ...

class Circle(Shape):
    def __init__(self, r: float):
        self.r = r
    def area(self) -&gt; float:
        return 3.14159 * self.r ** 2
    def perimeter(self) -&gt; float:
        return 2 * 3.14159 * self.r

# Shape()    # TypeError: Can't instantiate abstract class
c = Circle(5)
</code></pre>
<h3>@cached_property</h3>
<p dir="auto">Добавлен в Python 3.8. Вычисляет значение при первом обращении, кэширует его в <code>__dict__</code> экземпляра и больше не вызывает getter. В отличие от <code>@property</code> нет лишнего вызова при каждом обращении.</p>
<pre><code class="language-python">from functools import cached_property
import statistics

class Dataset:
    def __init__(self, data: list[float]):
        self._data = data

    @cached_property
    def stats(self) -&gt; dict:
        print("вычисляем...")   # выполнится только один раз
        return {
            "mean": statistics.mean(self._data),
            "stdev": statistics.stdev(self._data),
        }

ds = Dataset([1, 2, 3, 4, 5])
print(ds.stats)   # вычисляем...  {'mean': 3, 'stdev': 1.58...}
print(ds.stats)   # из кэша, без "вычисляем..."
</code></pre>
<p dir="auto">Важно: не работает с <code>__slots__</code> и не потокобезопасен без внешней блокировки.</p>
<h3>@override (Python 3.12+)</h3>
<p dir="auto">Из модуля <code>typing</code>. Сигнализирует тайп-чекеру, что метод переопределяет родительский. Если в родителе метод переименовали или удалили, тайп-чекер выдаст ошибку.</p>
<pre><code class="language-python">from typing import override

class Base:
    def process(self, data: str) -&gt; str:
        return data.upper()

class Child(Base):
    @override
    def process(self, data: str) -&gt; str:   # ошибка, если в Base нет process
        return data.lower()
</code></pre>
<h3>@deprecated (Python 3.13+)</h3>
<p dir="auto">Из модуля <code>warnings</code>. Помечает функцию или класс как устаревший: при вызове автоматически выдаётся <code>DeprecationWarning</code>, а тайп-чекеры отображают предупреждение.</p>
<pre><code class="language-python">from warnings import deprecated

@deprecated("Используйте new_api() вместо этого")
def old_api():
    return 42

old_api()   # DeprecationWarning: Используйте new_api() вместо этого
</code></pre>
<hr />
<h2>functools — арсенал высшего порядка</h2>
<h3>@lru_cache и @cache</h3>
<p dir="auto"><code>@lru_cache(maxsize=N)</code> кэширует результаты функции по аргументам (мемоизация), выбрасывая наименее использованные записи при достижении лимита. <code>@cache</code> (Python 3.9+) это <code>@lru_cache(maxsize=None)</code> без ограничения размера.</p>
<pre><code class="language-python">from functools import lru_cache, cache

@lru_cache(maxsize=128)
def fibonacci(n: int) -&gt; int:
    if n &lt; 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(50))               # мгновенно
print(fibonacci.cache_info())      # CacheInfo(hits=48, misses=51, maxsize=128, currsize=51)
fibonacci.cache_clear()            # сбросить кэш

# Для чистых функций без ограничения размера:
@cache
def factorial(n: int) -&gt; int:
    return n * factorial(n - 1) if n else 1
</code></pre>
<p dir="auto">Аргументы должны быть хэшируемыми. Список, словарь или другой изменяемый тип вызовет <code>TypeError</code>. Для методов экземпляра лучше использовать <code>@cached_property</code> или явно кэшировать через словарь.</p>
<h3>@singledispatch</h3>
<p dir="auto">Реализует перегрузку функций по типу первого аргумента (single dispatch, аналог pattern matching по типу).</p>
<pre><code class="language-python">from functools import singledispatch

@singledispatch
def process(value):
    raise TypeError(f"Неподдерживаемый тип: {type(value)}")

@process.register(int)
def _(value: int) -&gt; str:
    return f"Целое: {value * 2}"

@process.register(str)
def _(value: str) -&gt; str:
    return f"Строка: {value.upper()}"

@process.register(list)
@process.register(tuple)
def _(value) -&gt; str:
    return f"Последовательность длиной {len(value)}"

print(process(5))           # Целое: 10
print(process("hello"))     # Строка: HELLO
print(process([1, 2, 3]))   # Последовательность длиной 3
</code></pre>
<p dir="auto">Для методов классов используется <code>functools.singledispatchmethod</code> (Python 3.8+).</p>
<h3>@total_ordering</h3>
<p dir="auto">Если определить <code>__eq__</code> и один из методов сравнения (<code>__lt__</code>, <code>__le__</code>, <code>__gt__</code>, <code>__ge__</code>), <code>@total_ordering</code> автоматически выведет остальные три.</p>
<pre><code class="language-python">from functools import total_ordering

@total_ordering
class Version:
    def __init__(self, major: int, minor: int, patch: int):
        self.v = (major, minor, patch)

    def __eq__(self, other) -&gt; bool:
        return self.v == other.v

    def __lt__(self, other) -&gt; bool:
        return self.v &lt; other.v

v1 = Version(1, 2, 3)
v2 = Version(2, 0, 0)
print(v1 &lt; v2)    # True
print(v1 &gt;= v2)   # False (сгенерировано @total_ordering)
print(v2 &gt; v1)    # True  (сгенерировано @total_ordering)
</code></pre>
<hr />
<h2>Декораторы с параметрами</h2>
<p dir="auto">Чтобы передать аргументы в декоратор, нужен ещё один уровень вложенности: функция, возвращающая декоратор.</p>
<pre><code class="language-python">from functools import wraps
import time

def retry(max_attempts: int = 3, delay: float = 1.0, exceptions=(Exception,)):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            last_exc = None
            for attempt in range(1, max_attempts + 1):
                try:
                    return func(*args, **kwargs)
                except exceptions as e:
                    last_exc = e
                    print(f"Попытка {attempt}/{max_attempts} не удалась: {e}")
                    if attempt &lt; max_attempts:
                        time.sleep(delay)
            raise last_exc
        return wrapper
    return decorator

@retry(max_attempts=3, delay=0.5, exceptions=(ConnectionError,))
def fetch_data(url: str) -&gt; str:
    # симулируем нестабильное соединение
    import random
    if random.random() &lt; 0.7:
        raise ConnectionError("Нет соединения")
    return "data"
</code></pre>
<p dir="auto">Начиная с Python 3.8 можно сделать декоратор, работающий как с аргументами (<code>@retry(3)</code>), так и без (<code>@retry</code>), через параметр <code>func=None</code> и позиционно-ключевые аргументы:</p>
<pre><code class="language-python">def retry(_func=None, *, max_attempts: int = 3):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for _ in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except Exception:
                    pass
            raise RuntimeError("Все попытки исчерпаны")
        return wrapper

    if _func is not None:   # вызов без скобок: @retry
        return decorator(_func)
    return decorator        # вызов со скобками: @retry(max_attempts=5)
</code></pre>
<hr />
<h2>Стекирование декораторов</h2>
<p dir="auto">Несколько декораторов применяются снизу вверх: ближайший к функции оборачивает первым.</p>
<pre><code class="language-python">from functools import wraps

def bold(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return f"&lt;b&gt;{func(*args, **kwargs)}&lt;/b&gt;"
    return wrapper

def italic(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return f"&lt;i&gt;{func(*args, **kwargs)}&lt;/i&gt;"
    return wrapper

@bold
@italic
def greet(name: str) -&gt; str:
    return f"Привет, {name}!"

# Эквивалентно: greet = bold(italic(greet))
print(greet("Alice"))   # &lt;b&gt;&lt;i&gt;Привет, Alice!&lt;/i&gt;&lt;/b&gt;
</code></pre>
<p dir="auto">Порядок имеет значение. <code>@bold @italic</code> дают <code>&lt;b&gt;&lt;i&gt;...&lt;/i&gt;&lt;/b&gt;</code>, тогда как <code>@italic @bold</code> дадут <code>&lt;i&gt;&lt;b&gt;...&lt;/b&gt;&lt;/i&gt;</code>.</p>
<hr />
<h2>Декораторы классов</h2>
<p dir="auto">Декоратор может быть не только функцией, но и классом. В таком случае <code>__call__</code> становится телом обёртки, а <code>__init__</code> получает декорируемую функцию.</p>
<pre><code class="language-python">from functools import wraps, update_wrapper
import time

class Timer:
    """Декоратор-класс: замеряет время выполнения."""

    def __init__(self, func):
        self.func = func
        self.total_time = 0.0
        self.call_count = 0
        update_wrapper(self, func)  # аналог @wraps для классов

    def __call__(self, *args, **kwargs):
        start = time.perf_counter()
        result = self.func(*args, **kwargs)
        elapsed = time.perf_counter() - start
        self.total_time += elapsed
        self.call_count += 1
        print(f"{self.func.__name__}: {elapsed:.4f}с")
        return result

    def stats(self):
        avg = self.total_time / self.call_count if self.call_count else 0
        return {"total": self.total_time, "calls": self.call_count, "avg": avg}

@Timer
def slow_sum(n: int) -&gt; int:
    return sum(range(n))

slow_sum(1_000_000)
slow_sum(500_000)
print(slow_sum.stats())
</code></pre>
<p dir="auto">Декоратор-класс удобен, когда нужно хранить состояние между вызовами (счётчик, накопленное время, кэш).</p>
<hr />
<h2>Применение декоратора к классу целиком</h2>
<p dir="auto">Декоратор можно применить и к классу: Python передаёт объект класса, а не функции.</p>
<pre><code class="language-python">def add_repr(cls):
    """Добавляет __repr__, если его нет."""
    if "__repr__" not in cls.__dict__:
        def __repr__(self):
            attrs = ", ".join(
                f"{k}={v!r}" for k, v in self.__dict__.items()
            )
            return f"{cls.__name__}({attrs})"
        cls.__repr__ = __repr__
    return cls

@add_repr
class Config:
    def __init__(self, host: str, port: int):
        self.host = host
        self.port = port

print(Config("localhost", 8080))   # Config(host='localhost', port=8080)
</code></pre>
<hr />
<h2>Практические паттерны</h2>
<h3>Логирование</h3>
<pre><code class="language-python">import logging
from functools import wraps

def log_calls(logger=None, level=logging.DEBUG):
    _logger = logger or logging.getLogger(__name__)
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            _logger.log(level, "Вызов %s args=%s kwargs=%s", func.__name__, args, kwargs)
            try:
                result = func(*args, **kwargs)
                _logger.log(level, "%s вернул %r", func.__name__, result)
                return result
            except Exception as exc:
                _logger.exception("%s выбросил %s", func.__name__, exc)
                raise
        return wrapper
    return decorator

logging.basicConfig(level=logging.DEBUG)

@log_calls()
def divide(a: float, b: float) -&gt; float:
    return a / b
</code></pre>
<h3>Rate Limiter</h3>
<pre><code class="language-python">import time
from collections import deque
from functools import wraps

def rate_limit(calls: int, period: float):
    """Не более `calls` вызовов за `period` секунд."""
    def decorator(func):
        timestamps: deque = deque()
        @wraps(func)
        def wrapper(*args, **kwargs):
            now = time.monotonic()
            while timestamps and now - timestamps &gt;= period:
                timestamps.popleft()
            if len(timestamps) &gt;= calls:
                sleep_for = period - (now - timestamps)
                time.sleep(sleep_for)
            timestamps.append(time.monotonic())
            return func(*args, **kwargs)
        return wrapper
    return decorator

@rate_limit(calls=5, period=1.0)
def call_api(endpoint: str) -&gt; dict:
    return {"endpoint": endpoint, "ok": True}
</code></pre>
<h3>Singleton</h3>
<pre><code class="language-python">from functools import wraps
import threading

def singleton(cls):
    instances = {}
    lock = threading.Lock()

    @wraps(cls, updated=[])
    def get_instance(*args, **kwargs):
        if cls not in instances:
            with lock:
                if cls not in instances:  # double-checked locking
                    instances[cls] = cls(*args, **kwargs)
        return instances[cls]

    return get_instance

@singleton
class DatabasePool:
    def __init__(self, dsn: str = "sqlite:///:memory:"):
        self.dsn = dsn
        print(f"Создан пул: {dsn}")

a = DatabasePool("postgresql://localhost/db")
b = DatabasePool()   # "Создан пул" не печатается второй раз
print(a is b)        # True
</code></pre>
<h3>Валидация типов</h3>
<pre><code class="language-python">import inspect
from functools import wraps

def validate_types(func):
    sig = inspect.signature(func)
    hints = func.__annotations__

    @wraps(func)
    def wrapper(*args, **kwargs):
        bound = sig.bind(*args, **kwargs)
        bound.apply_defaults()
        for name, value in bound.arguments.items():
            if name in hints and name != "return":
                expected = hints[name]
                if not isinstance(value, expected):
                    raise TypeError(
                        f"Параметр '{name}': ожидался {expected.__name__}, "
                        f"получен {type(value).__name__}"
                    )
        return func(*args, **kwargs)
    return wrapper

@validate_types
def power(base: float, exp: int) -&gt; float:
    return base ** exp

power(2.0, 3)    # OK
power(2.0, 3.5)  # TypeError: Параметр 'exp': ожидался int, получен float
</code></pre>
<hr />
<h2>Асинхронные декораторы</h2>
<p dir="auto">Декоратор не знает заранее, синхронная функция или async. Для универсального декоратора нужно проверять это явно.</p>
<pre><code class="language-python">import asyncio
import time
import inspect
from functools import wraps

def timeit(func):
    @wraps(func)
    async def async_wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = await func(*args, **kwargs)
        print(f"{func.__name__}: {time.perf_counter() - start:.4f}с (async)")
        return result

    @wraps(func)
    def sync_wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        print(f"{func.__name__}: {time.perf_counter() - start:.4f}с (sync)")
        return result

    # Выбираем обёртку один раз при декорировании (не при каждом вызове)
    return async_wrapper if inspect.iscoroutinefunction(func) else sync_wrapper

@timeit
def cpu_task(n: int) -&gt; int:
    return sum(range(n))

@timeit
async def io_task(delay: float) -&gt; str:
    await asyncio.sleep(delay)
    return "done"

cpu_task(10_000_000)
asyncio.run(io_task(0.1))
</code></pre>
<p dir="auto">Проверка <code>inspect.iscoroutinefunction</code> выполняется один раз при декорировании, а не при каждом вызове, что исключает лишние расходы в рантайме.</p>
<hr />
<h2>Декораторы в контексте Python 3.13 и 3.14</h2>
<p dir="auto">В Python 3.13 появился <code>@typing.deprecated</code> для явной пометки устаревшего кода прямо в системе типов. В Python 3.14 аннотации стали отложенными (PEP 649): они не вычисляются при определении функции. Это означает, что декораторы, читающие <code>func.__annotations__</code> напрямую (например, <code>validate_types</code> выше), теперь получат объект <code>AnnotateFunc</code>, а не уже вычисленный словарь. Для надёжной работы с аннотациями используйте <code>inspect.get_annotations(func, eval_str=True)</code>:</p>
<pre><code class="language-python">import inspect

def validate_types(func):
    sig = inspect.signature(func)
    # eval_str=True вычисляет строковые аннотации и отложенные (3.14+)
    hints = inspect.get_annotations(func, eval_str=True)
    ...
</code></pre>
<hr />
<h2>Шпаргалка — когда что использовать</h2>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Задача</th>
<th>Декоратор</th>
</tr>
</thead>
<tbody>
<tr>
<td>Управляемый атрибут</td>
<td><code>@property</code></td>
</tr>
<tr>
<td>Метод без <code>self</code>/<code>cls</code></td>
<td><code>@staticmethod</code></td>
</tr>
<tr>
<td>Альтернативный конструктор</td>
<td><code>@classmethod</code></td>
</tr>
<tr>
<td>Автогенерация boilerplate</td>
<td><code>@dataclass</code></td>
</tr>
<tr>
<td>Абстрактный интерфейс</td>
<td><code>@abstractmethod</code></td>
</tr>
<tr>
<td>Ленивое кэширование атрибута</td>
<td><code>@cached_property</code></td>
</tr>
<tr>
<td>Мемоизация чистых функций</td>
<td><code>@lru_cache</code> / <code>@cache</code></td>
</tr>
<tr>
<td>Перегрузка по типу</td>
<td><code>@singledispatch</code></td>
</tr>
<tr>
<td>Вывод методов сравнения</td>
<td><code>@total_ordering</code></td>
</tr>
<tr>
<td>Пометить как устаревший</td>
<td><code>@deprecated</code> (3.13+)</td>
</tr>
<tr>
<td>Явное переопределение метода</td>
<td><code>@override</code> (3.12+)</td>
</tr>
<tr>
<td>Сохранить метаданные функции</td>
<td><code>@wraps</code></td>
</tr>
</tbody>
</table>
<hr />
<h2>Полезные источники</h2>
<ul>
<li><strong>Официальная документация <code>functools</code></strong> — <a href="http://docs.python.org/3/library/functools.html" target="_blank" rel="noopener noreferrer">docs.python.org/3/library/functools.html</a></li>
<li><strong>“What’s New In Python 3.13”</strong> — <a href="http://docs.python.org/3/whatsnew/3.13.html" target="_blank" rel="noopener noreferrer">docs.python.org/3/whatsnew/3.13.html</a> (про <code>@deprecated</code>, <code>@override</code>)</li>
<li><strong>“What’s New In Python 3.14”</strong> — <a href="http://docs.python.org/3/whatsnew/3.14.html" target="_blank" rel="noopener noreferrer">docs.python.org/3/whatsnew/3.14.html</a> (про отложенные аннотации PEP 649)</li>
<li><strong>Real Python — Primer on Python Decorators</strong> — <a href="http://realpython.com/primer-on-python-decorators/" target="_blank" rel="noopener noreferrer">realpython.com/primer-on-python-decorators/</a></li>
<li><strong>freeCodeCamp — The Python Decorator Handbook</strong> — <a href="http://freecodecamp.org/news/the-python-decorator-handbook/" target="_blank" rel="noopener noreferrer">freecodecamp.org/news/the-python-decorator-handbook/</a></li>
<li><strong><a href="http://asyncmove.com" target="_blank" rel="noopener noreferrer">asyncmove.com</a> — The Comprehensive Guide to Python Decorators</strong> (2026) — <a href="http://asyncmove.com/blog/2026/01/the-comprehensive-guide-to-python-decorators/" target="_blank" rel="noopener noreferrer">asyncmove.com/blog/2026/01/the-comprehensive-guide-to-python-decorators/</a></li>
<li><strong>PEP 318</strong> — декораторы функций и методов</li>
<li><strong>PEP 614</strong> — расслабление грамматических ограничений на декораторы (3.9+)</li>
<li><strong>PEP 649</strong> — отложенные аннотации (3.14)</li>
</ul>
<hr />
<h2>References</h2>
<ol>
<li>
<p dir="auto"><a href="https://realpython.com/primer-on-python-decorators/" target="_blank" rel="noopener noreferrer">Fancy Decorators</a> - In this tutorial, you’ll look at what Python decorators are and how you define and use them. Decorat…</p>
</li>
<li>
<p dir="auto"><a href="https://www.freecodecamp.org/news/the-python-decorator-handbook/" target="_blank" rel="noopener noreferrer">The Python Decorator Handbook - freeCodeCamp</a> - Python decorators provide an easy yet powerful syntax for modifying and extending the behavior of fu…</p>
</li>
<li>
<p dir="auto"><a href="https://docs.python.org/3/library/functools.html" target="_blank" rel="noopener noreferrer">functools — Higher-order functions and operations on callable …</a> - The functools module is for higher-order functions: functions that act on or return other functions…</p>
</li>
<li>
<p dir="auto"><a href="https://blog.devgenius.io/exploring-pythons-lesser-known-standard-libraries-670c509bf062" target="_blank" rel="noopener noreferrer">Python Standard Library | collections | itertools | functools - Dev Genius</a> - Python’s functools module, part of the standard library, provides higher-order functions and operati…</p>
</li>
<li>
<p dir="auto"><a href="https://python.plainenglish.io/handy-python-decorators-f65fd81cf2dc" target="_blank" rel="noopener noreferrer">Handy Python Decorators. Implementing and Using Advanced…</a> - The Python Standard Library contains four incredibly useful decorators: staticmethod , classmethod ,…</p>
</li>
<li>
<p dir="auto"><a href="https://docs.python.org/3/whatsnew/3.13.html" target="_blank" rel="noopener noreferrer">What’s New In Python 3.13 — Python 3.14.4 documentation</a> - The biggest changes include a new interactive interpreter, experimental support for running in a fre…</p>
</li>
<li>
<p dir="auto"><a href="https://docs.python.org/3.14/whatsnew/3.13.html" target="_blank" rel="noopener noreferrer">What’s New In Python 3.13 — Python 3.14.0rc3 documentation</a> - Editors, Adam Turner and Thomas Wouters,. This article explains the new features in Python 3.13, com…</p>
</li>
<li>
<p dir="auto"><a href="https://binaryscripts.com/python/2024/12/03/mastering-python-decorators-for-code-reusability-and-optimization.html" target="_blank" rel="noopener noreferrer">Mastering Python Decorators for Code Reusability and Optimization</a> - Discover how Python decorators can help you write reusable, efficient, and maintainable code by modi…</p>
</li>
<li>
<p dir="auto"><a href="https://asyncmove.com/blog/2026/01/the-comprehensive-guide-to-python-decorators/" target="_blank" rel="noopener noreferrer">The Comprehensive Guide to Python Decorators</a> - Decorators are one of Python’s most powerful metaprogramming tools, widely used in the industry to w…</p>
</li>
<li>
<p dir="auto"><a href="https://docs.python.org/3/whatsnew/3.14.html" target="_blank" rel="noopener noreferrer">What’s new in Python 3.14 — Python 3.14.4 documentation</a> - This article explains the new features in Python 3.14, compared to 3.13. … no_type_check_decorator…</p>
</li>
</ol>
]]></description><link>https://forum.exlends.com/topic/2220/poleznye-dekoratory-v-python-ischerpyvayushij-gajd</link><generator>RSS for Node</generator><lastBuildDate>Sat, 02 May 2026 13:15:53 GMT</lastBuildDate><atom:link href="https://forum.exlends.com/topic/2220.rss" rel="self" type="application/rss+xml"/><pubDate>Thu, 30 Apr 2026 14:18:19 GMT</pubDate><ttl>60</ttl></channel></rss>