1. `@classmethod` и `@staticmethod`

1. `@classmethod` и `@staticmethod`


Стандартные декораторы и инструменты Python: руководство с примерами

В Python стандартная библиотека предоставляет множество полезных декораторов и инструментов, упрощающих разработку. Рассмотрим ключевые из них: их назначение, синтаксис и ограничения.


1. @classmethod и @staticmethod

  • Назначение:

    • @classmethod превращает метод в метод класса. Первый аргумент — сам класс (cls). Используется для создания фабричных методов или работы с классом, а не экземпляром.
    • @staticmethod определяет статический метод. Не получает ни self, ни cls. Это обычная функция, но внутри класса для логической группировки.
  • Синтаксис:

    class MyClass:
        @classmethod
        def class_method(cls, arg1):
            print(f"Вызван метод класса {cls} с аргументом {arg1}")
    
        @staticmethod
        def static_method(arg1):
            print(f"Статический метод с аргументом {arg1}")
  • Ограничения:

    • @classmethod не может быть использован для изменения состояния экземпляра (не имеет доступа к self).
    • @staticmethod не взаимодействует ни с классом, ни с экземпляром.

2. @property, геттеры, сеттеры и делитеры

  • Назначение:
    Позволяет управлять доступом к атрибутам класса, добавляя логику при чтении, записи или удалении.

  • Синтаксис:

    class Person:
        def __init__(self, name):
            self._name = name  # Приватный атрибут
    
        @property
        def name(self):
            return self._name
    
        @name.setter
        def name(self, value):
            if not isinstance(value, str):
                raise ValueError("Имя должно быть строкой")
            self._name = value
    
        @name.deleter
        def name(self):
            print("Удаление имени")
            del self._name
  • Ограничения:

    • Геттер обязателен. Сеттер и делитер опциональны.
    • Нельзя использовать для асинхронных методов.

3. @functools.wraps

  • Назначение:
    Сохраняет метаданные оригинальной функции (например, имя и документацию) при использовании декораторов.

  • Пример:

    import functools
    
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print("До вызова функции")
            result = func(*args, **kwargs)
            print("После вызова")
            return result
        return wrapper
    
    @decorator
    def example():
        """Документация функции."""
        pass
    
    print(example.__name__)  # "example", а не "wrapper"
    print(example.__doc__)   # Документация сохраняется

4. Кеширование: @cached_property, @lru_cache

  • @functools.cached_property (Python 3.8+):
    Кеширует результат метода класса как атрибут. Вычисляется один раз за время жизни экземпляра.

    from functools import cached_property
    
    class Data:
        def __init__(self, values):
            self.values = values
    
        @cached_property
        def stats(self):
            return {"sum": sum(self.values)}
  • @functools.lru_cache:
    Кеширует результаты функции с ограничением по размеру (Least Recently Used).

    from functools import lru_cache
    
    @lru_cache(maxsize=128)
    def factorial(n):
        return n * factorial(n-1) if n else 1
  • Ограничения:

    • Аргументы функции должны быть хешируемыми.
    • cached_property не подходит для изменяемых данных.

5. @contextlib.contextmanager

  • Назначение:
    Создает контекстный менеджер для управления ресурсами (например, файлами) с использованием генератора.

  • Пример:

    from contextlib import contextmanager
    
    @contextmanager
    def open_file(path, mode):
        file = open(path, mode)
        try:
            yield file
        finally:
            file.close()
    
    # Использование:
    with open_file("test.txt", "w") as f:
        f.write("Hello!")

6. @functools.total_ordering

  • Назначение:
    Автоматически генерирует недостающие методы сравнения (<, >, <=, >=) на основе __eq__ и одного из методов (__lt__, __le__, __gt__, __ge__).

  • Пример:

    from functools import total_ordering
    
    @total_ordering
    class Point:
        def __init__(self, x, y):
            self.x = x
            self.y = y
    
        def __eq__(self, other):
            return (self.x, self.y) == (other.x, other.y)
    
        def __lt__(self, other):
            return (self.x + self.y) < (other.x + other.y)

7. @functools.singledispatch

  • Назначение:
    Позволяет создавать перегруженные функции (разные реализации для разных типов аргументов).

  • Пример:

    from functools import singledispatch
    
    @singledispatch
    def process(arg):
        print("Аргумент по умолчанию:", arg)
    
    @process.register(int)
    def _(arg):
        print("Целое число:", arg)
    
    @process.register(list)
    def _(arg):
        print("Список, длина:", len(arg))
    
    process(10)      # Целое число: 10
    process([1,2,3]) # Список, длина: 3

Итог

Перечисленные инструменты решают распространенные задачи: управление методами классов, безопасный доступ к атрибутам, кеширование, управление контекстом и упрощение сравнения объектов. Их правильное использование делает код чище, эффективнее и легче в поддержке. Однако важно учитывать их ограничения (например, требования к хешируемости аргументов для lru_cache или неизменяемость данных для cached_property).