這有點棘手,因爲cached_property
和hybrid_property
希望包裝一個方法並返回一個屬性。你最終擴展其中一個或兩個。
我能想出的最好的東西就是這個。它基本上將cached_property
的邏輯劃入hybrid_property
的__get__
。請注意,它會緩存實例的屬性值,但不會爲該類緩存。
from sqlalchemy.ext.hybrid import hybrid_property
_missing = object() # sentinel object for missing values
class cached_hybrid_property(hybrid_property):
def __get__(self, instance, owner):
if instance is None:
# getting the property for the class
return self.expr(owner)
else:
# getting the property for an instance
name = self.fget.__name__
value = instance.__dict__.get(name, _missing)
if value is _missing:
value = self.fget(instance)
instance.__dict__[name] = value
return value
class Example(object):
@cached_hybrid_property
def foo(self):
return "expensive calculations"
起初我還以爲你可以簡單地使用functools.lru_cache
,而不是cached_property
。然後我意識到您可能需要一個實例特定的緩存而不是實例索引的全局緩存,這就是lru_cache
提供的緩存。沒有用於每個實例緩存方法調用的標準庫實用程序。
爲了說明問題lru_cache
,考慮緩存的這個簡單的版本:
CACHE = {}
class Example(object):
@property
def foo(self):
if self not in CACHE:
CACHE[self] = ... # do the actual computation
return CACHE[self]
這將存儲的foo
緩存值,每Example
比如你的程序生成 - 換言之,它可以泄漏內存。 lru_cache
稍微聰明一點,因爲它限制了緩存的大小,但是如果它們離開緩存的話,最終可能會重新計算所需的一些值。更好的解決方案是將緩存的值附加到它們所屬的Example
的實例,如cached_property
所做的那樣。
謝謝,很好的答案。你能稍微擴展你的答案來澄清這些差異嗎? 「一個特定於實例的緩存,而不是實例索引的全局緩存」 –