Введение
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
- Импортируйте декоратор
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метод будет вычислять факториал при каждом его вызове.
Показатели эффективности:
Когда использовать:
- Вы хотите избежать избыточных вычислений:
cached_property
кэширует дорогостоящие вычисления свойств, чтобы предотвратить избыточные и дорогостоящие вычисления. - Вам нужен более чистый и эффективный код:
cached_property
упрощает код, автоматически управляя кэшированием. Он заменяет необходимость в методах ручного запоминания, делая ваш код более читабельным и удобным в сопровождении. - Вы хотите сбалансировать память и производительность:Кэшируя результат свойства,
cached_property
оптимизирует использование памяти для повышения производительности вычислений. Этот компромисс может быть ценным, когда память не является критическим ограничением, а эффективность вычислений.
Когда не использовать
- Жесткие ограничения памяти. Иногда использование кэшированного_свойства может занимать много памяти. Если вам нужно сэкономить память, возможно, лучше не использовать ее, даже если это повысит производительность.
Устранение ловушек:
- Динамические или часто меняющиеся данные. Если данные, лежащие в основе свойства, изменятся, это не отразится в кэшированном_свойстве.
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
. Я призываю вас экспериментировать, учиться и применять эти концепции в своих проектах.
Также, если у вас есть другие/еще идеи, поделитесь ими в комментариях, тогда я смогу обновить статью.
Спасибо за чтение и удачного кодирования!