Какие основные встроенные декораторы доступны в Python?

Ответ

В Python декораторы — это функции, которые изменяют или расширяют поведение других функций или методов без изменения их исходного кода. Вот основные встроенные декораторы и те, что часто используются из стандартной библиотеки functools:

  1. @property

    • Назначение: Позволяет превратить метод класса в атрибут, обеспечивая контролируемый доступ к данным (геттер).
    • Почему: Используется для инкапсуляции, создания "вычисляемых" атрибутов и обеспечения валидации при установке значений (через @<attr>.setter). Улучшает читаемость кода, позволяя обращаться к методам как к атрибутам.
    • Пример:

      class Circle:
          def __init__(self, radius):
              self._radius = radius # Приватный атрибут
      
          @property
          def radius(self):
              """Геттер для радиуса."""
              return self._radius
      
          @radius.setter
          def radius(self, value):
              """Сеттер для радиуса с валидацией."""
              if value < 0:
                  raise ValueError("Радиус не может быть отрицательным")
              self._radius = value
      
      c = Circle(10)
      print(c.radius) # Вызывает геттер: 10
      c.radius = 15   # Вызывает сеттер
      print(c.radius) # 15
      # c.radius = -5 # Вызовет ValueError
  2. @classmethod

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

      class MyClass:
          def __init__(self, data):
              self.data = data
      
          @classmethod
          def from_string(cls, string_data):
              """Фабричный метод для создания экземпляра из строки."""
              return cls(int(string_data))
      
          @classmethod
          def default_instance(cls):
              """Создает экземпляр с данными по умолчанию."""
              return cls(0)
      
      obj1 = MyClass(10)
      obj2 = MyClass.from_string("20")
      obj3 = MyClass.default_instance()
      print(obj1.data, obj2.data, obj3.data) # Вывод: 10 20 0
  3. @staticmethod

    • Назначение: Объявляет метод как статический. Он не принимает ни self, ни cls в качестве первого аргумента и ведет себя как обычная функция, логически связанная с классом.
    • Почему: Используется для утилитарных функций, которые не зависят от состояния экземпляра или класса, но логически принадлежат классу. Помогает организовать код.
    • Пример:

      class MathUtils:
          @staticmethod
          def add(a, b):
              """Статический метод для сложения двух чисел."""
              return a + b
      
          @staticmethod
          def multiply(a, b):
              """Статический метод для умножения двух чисел."""
              return a * b
      
      print(MathUtils.add(5, 3))      # Вывод: 8
      print(MathUtils.multiply(5, 3)) # Вывод: 15
  4. @functools.cache (Python 3.9+)

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

      import functools
      import time
      
      @functools.cache
      def fibonacci(n):
          if n <= 1:
              return n
          return fibonacci(n - 1) + fibonacci(n - 2)
      
      start_time = time.time()
      fibonacci(30) # Вычисляется один раз
      print(f"Time for first call: {time.time() - start_time:.4f}s")
      
      start_time = time.time()
      fibonacci(30) # Результат берется из кеша
      print(f"Time for second call: {time.time() - start_time:.4f}s")
  5. @functools.lru_cache

    • Назначение: Декоратор для кеширования результатов функции с ограничением по размеру (Least Recently Used - наименее недавно использованные).
    • Почему: Позволяет управлять потреблением памяти кешем, автоматически удаляя наименее недавно использованные элементы, когда кеш достигает максимального размера. Идеален для функций, работающих с ограниченным набором часто используемых входных данных.
    • Пример:

      import functools
      
      @functools.lru_cache(maxsize=128) # Кеш на 128 последних результатов
      def get_web_page(url):
          """Имитация дорогостоящего запроса к веб-странице."""
          print(f"Fetching {url}...")
          # time.sleep(1) # Имитация задержки
          return f"Content of {url}"
      
      print(get_web_page("http://example.com/page1")) # Вывод: Fetching http://example.com/page1...
      print(get_web_page("http://example.com/page2")) # Вывод: Fetching http://example.com/page2...
      print(get_web_page("http://example.com/page1")) # Результат берется из кеша, без 'Fetching...'