Введение

cached_propertyэто сочетание ленивой оценки и запоминания

В мире программирования на Python оптимизация производительности кода является важнейшей задачей. Независимо от того, создаете ли вы веб-приложение, обрабатываете данные или работаете над каким-либо проектом Python, эффективность вашего кода может существенно повлиять на скорость выполнения и использование ресурсов. Именно здесь Python cached_property вступает в игру как мощный инструмент оптимизации производительности.

cached_property — это комбинация двух мощных концепций Python: ленивая оценка и запоминание. Давайте углубимся в то, как cached_property объединяет оба этих метода, в следующих разделах.

Что такое кэшированное_свойство

  • cached_property — это декоратор, предоставляемый functools, который входит в состав стандартной библиотеки Python из версии Python 3.8.
  • Основная цель cached_property — эффективно кэшировать (сохранять) результат вычисления свойства после его первого вычисления.

Вот суть того, как cached_property работает

  • Подобно обычным methods, staticmethod и property, вычисление, связанное с cached_property, запускается только тогда, когда вы явно вызываете атрибут с точечной записью.
  • Как только вызывается атрибут с cached_property, выполняются вычисления, связанные с атрибутом, и результат сохраняется в кэше внутри объекта.
  • Последующие обращения к свойству возвращают кэшированный результат без повторного запуска вычислений.

По сути, cached_property сочетает в себе лучшее из обоих миров: динамическое поведение свойств и эффективность кэширования. Это позволяет вам писать чистый, читаемый код, гарантируя, что производительность вашего приложения не пострадает из-за избыточных вычислений.

Как использовать cached_property

  1. Импортируйте декоратор cached_property из модуля functools.
 from functools import cached_property

2. Украсьте метод @cached_property. Этот декоратор преобразует метод в кэшированное свойство. Метод должен выполнять вычисления, которые вы хотите кэшировать.

class MyClass:
  @cached_property
  def do_expensive_calculation(self):
      ...

In the above example do_expensive_calculation will be computed only once. Subsequent read will return the cached result.

Сравнение property и cached_property:

  • ЗАПОМИНАНИЕ:Инструкции/код в атрибуте property будут выполняться при каждом их вызове. Где, как в cached_property, инструкция/код будет выполняться только один раз.
  • НАЗНАЧЕНИЕ ЗНАЧЕНИЯ. Ключевое различие между свойством и кэшированным_свойством заключается в том, что атрибут property не позволяет присваивать значение, если не определен установщик. Тогда как в cached_property по по умолчанию мы можем назначать значение, как и другие атрибуты экземпляра.

Сравнение производительности:

from functools import cached_property
class Calculate:

  def __init__(self, number):
    self.number = number
  
  @property
  def factorial_property(self):
     product = 1
     for num in range(2, self.number):
        product *= num
     return product
  
  @cached_property
  def cached_factorial(self):
      return self.factorial_property

Здесь мы можем наблюдать

1. Метод cached_factorial вызывает факториал_свойство, поэтому время, затрачиваемое этим методом, равно времени, затраченному на кэширование, + время, затраченное на факториал_свойство

Время кэширования составляет в среднем 0,0002 секунды (для 100 раз).

Итак, для cached_factorial

time_taken(first_time) =0.0002 + time_taken_by_factorial(for one time)

time_taken(subsequent_calls) = 0.0006

2.Factorial_propertyметод будет вычислять факториал при каждом его вызове.

Показатели эффективности:

Когда использовать:

  1. Вы хотите избежать избыточных вычислений:cached_propertyкэширует дорогостоящие вычисления свойств, чтобы предотвратить избыточные и дорогостоящие вычисления.
  2. Вам нужен более чистый и эффективный код: cached_property упрощает код, автоматически управляя кэшированием. Он заменяет необходимость в методах ручного запоминания, делая ваш код более читабельным и удобным в сопровождении.
  3. Вы хотите сбалансировать память и производительность:Кэшируя результат свойства, cached_property оптимизирует использование памяти для повышения производительности вычислений. Этот компромисс может быть ценным, когда память не является критическим ограничением, а эффективность вычислений.

Когда не использовать

  1. Жесткие ограничения памяти. Иногда использование кэшированного_свойства может занимать много памяти. Если вам нужно сэкономить память, возможно, лучше не использовать ее, даже если это повысит производительность.

Устранение ловушек:

  1. Динамические или часто меняющиеся данные. Если данные, лежащие в основе свойства, изменятся, это не отразится в кэшированном_свойстве.
from functools import cached_property
class Calculate:

    def __init__(self, number):
        self.number = number

    @cached_property #cached_property applied
    def calculate_factorial(self):
        product = 1
        for num in range(2, self.number + 1):
            product *= num
        return product

    def add_number_and_get_factorial(self, update_num):
        self.number += update_num
        return self.calculate_factorial


calculation = Calculate(number=5)
print(calculation.calculate_factorial) # 120
print(calculation.add_number_and_get_factorial(1)) # Expected 720
                                                      # Got 120
# --------------------------- FIX ----------------------------------------- #
"""
If you want to run the cached_property method again. 
You need to remove cached value by deleting the attribute.
"""
del calculation.calculate_factorial  # this will allow the method to run again

2. Переопределение атрибута. В отличие от property, по умолчанию cached_property можно переопределить.

from functools import cached_property
class Calculate:

    def __init__(self, number):
        self.number = number

    @cached_property
    def calculate_factorial(self):
        product = 1
        for num in range(2, self.number + 1):
            product *= num
        return product

    def add_number_and_get_factorial(self, update_num):
        """
          Here, cached_property is overridden. 
        """
        self.calculate_factorial = update_num # updating cached_property
        return self.calculate_factorial
    
    

calculation = Calculate(number=5)
print(calculation.add_number_and_get_factorial(1)) # Expected 720
                                                   # Got 1

# -------------------------------- FIX ----------------------------------- #
"""
Fix - If you want to make your cached_property readable you need to write your 
own cached_property. 

For Example
"""


class ReadOnlyCachedProperty(cached_property):
    def __set__(self, *args, **kwargs):
      raise AttributeError()

# ----------------------- FIX IMPLEMENTATION --------------------------- #
from functools import cached_property
class Calculate:

    def __init__(self, number):
        self.number = number
    
    @ReadOnlyCachedProperty # Cached property applied
    def calculate_factorial(self):
        product = 1
        for num in range(2, self.number + 1):
            product *= num
        return product

    def add_number_and_get_factorial(self, update_num):
        """
          Here, cached_property is overridden. 
        """
        self.calculate_factorial = update_num. # This will raise Attribute Error
        return self.calculate_factorial

calculation = Calculate(number=5)
print(calculation.add_number_and_get_factorial(1))

Ссылка: Документация Python

ЗАКЛЮЧЕНИЕ:

  • Декоратор cached_property в Python 3.8 позволяет эффективно кэшировать вычисления свойств, оптимизируя производительность кода.
  • Это полезно для предотвращения избыточных вычислений и улучшения использования памяти и эффективности вычислений.
  • Однако он может быть не идеальным для низкой частоты доступа к атрибутам или динамических данных.
  • Для решения потенциальных проблем можно создать собственные декораторы cached_property.
  • В целом, понимание кэшированного_свойства может привести к созданию более эффективных приложений Python.

Я надеюсь, что эти идеи пролили свет на возможности cached_property в Python и на то, как он может помочь вам оптимизировать ваш код для повышения производительности.

Поскольку технологии развиваются, а Python продолжает расти, крайне важно быть в курсе лучших практик и таких инструментов, как cached_property. Я призываю вас экспериментировать, учиться и применять эти концепции в своих проектах.

Также, если у вас есть другие/еще идеи, поделитесь ими в комментариях, тогда я смогу обновить статью.

Спасибо за чтение и удачного кодирования!