Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Вивчайте Caching with cached_property | Building Useful Descriptors
Python Descriptors Explained

Caching with cached_property

Свайпніть щоб показати меню

functools.cached_property is a built-in non-data descriptor that implements lazy loading with a clean decorator syntax. It computes a value on first access, stores it in the instance __dict__, and returns the cached result on every subsequent access – with no extra code required.

Basic Usage

123456789101112131415161718192021222324252627
import functools import time # cached_property computes once and caches in instance __dict__ class InvestmentPortfolio: def __init__(self, portfolio_id, positions): self.portfolio_id = portfolio_id self.positions = positions # List of (ticker, quantity, price) tuples @functools.cached_property def total_value(self): time.sleep(0.1) # Simulating expensive valuation return sum(quantity * price for _, quantity, price in self.positions) @functools.cached_property def position_count(self): return len(self.positions) portfolio = InvestmentPortfolio("PF-001", [ ("AAPL", 100, 189.50), ("MSFT", 50, 415.20), ("GOOG", 25, 175.80), ]) print(portfolio.total_value) # Computed – ~0.1s print(portfolio.total_value) # Cached – instant print("total_value" in portfolio.__dict__) # True – stored in instance dict

How It Works Under the Hood

cached_property is a non-data descriptor. Its __get__ computes the value, stores it in obj.__dict__ under the same name, and returns it. On the next access, the instance attribute is found first – before the descriptor is even consulted:

123
# Inspecting cached_property as a descriptor print(hasattr(functools.cached_property, "__get__")) # True print(hasattr(functools.cached_property, "__set__")) # False – non-data descriptor

Invalidating the Cache

Because the cached value lives in __dict__, you can invalidate it with del:

12345678910111213141516171819
import functools class PricingModel: def __init__(self, model_id, base_price): self.model_id = model_id self.base_price = base_price self._discount = 0.0 @functools.cached_property def final_price(self): return self.base_price * (1 - self._discount) model = PricingModel("M-001", 1000.0) print(model.final_price) # 1000.0 – cached # Updating the discount and invalidating the cache model._discount = 0.15 del model.final_price # Removing from __dict__ print(model.final_price) # 850.0 – recomputed with new discount

Thread Safety

cached_property is not thread-safe by default. In a multi-threaded environment, two threads can both compute the value simultaneously before either stores it. For thread-safe caching, use a threading.Lock:

1234567891011121314151617
import functools import threading import time class RiskCalculator: def __init__(self, portfolio_id): self.portfolio_id = portfolio_id self._lock = threading.Lock() @functools.cached_property def risk_score(self): with self._lock: # Checking again inside the lock – another thread may have computed it if "risk_score" in self.__dict__: return self.__dict__["risk_score"] time.sleep(0.05) # Simulating computation return round(hash(self.portfolio_id) % 100 / 10, 2)

cached_property vs lru_cache

Use cached_property when the cached value depends on instance state and each instance needs its own cache. Use lru_cache for pure functions or class-level caching shared across instances.

question mark

How do you invalidate the cache of a functools.cached_property attribute?

Виберіть правильну відповідь

Все було зрозуміло?

Як ми можемо покращити це?

Дякуємо за ваш відгук!

Секція 3. Розділ 3

Запитати АІ

expand

Запитати АІ

ChatGPT

Запитайте про що завгодно або спробуйте одне із запропонованих запитань, щоб почати наш чат

Секція 3. Розділ 3
some-alt