Jasne, tylko że nieruchomość ustawiony atrybut instancją, która jest zwracana na późniejsze dostępu:
class Foo(object):
_cached_bar = None
@property
def bar(self):
if not self._cached_bar:
self._cached_bar = self._get_expensive_bar_expression()
return self._cached_bar
property
deskryptora jest deskryptorem danych (implementuje haki deskryptora), więc będzie wywoływany, nawet jeśli na instancji istnieje atrybut bar
, z wynikiem końcowym, że Python zignoruje ten atrybut, stąd potrzeba przetestowania osobnego atrybutu attrib przy każdym dostępie.
Możesz napisać własny deskryptor że tylko implementuje __get__
, w którym momencie Python używa atrybutu na przykład na deskryptorze jeśli istnieje:
class CachedProperty(object):
def __init__(self, func, name=None):
self.func = func
self.name = name if name is not None else func.__name__
self.__doc__ = func.__doc__
def __get__(self, instance, class_):
if instance is None:
return self
res = self.func(instance)
setattr(instance, self.name, res)
return res
class Foo(object):
@CachedProperty
def bar(self):
return self._get_expensive_bar_expression()
Jeśli wolisz podejście __getattr__
(który ma coś do mówią o tym), że byłoby:
class Foo(object):
def __getattr__(self, name):
if name == 'bar':
bar = self.bar = self._get_expensive_bar_expression()
return bar
return super(Foo, self).__getattr__(name)
Późniejsza dostęp znajdzie atrybut bar
na przykład i __getattr__
nie będzie konsultowany.
Demo:
>>> class FooExpensive(object):
... def _get_expensive_bar_expression(self):
... print 'Doing something expensive'
... return 'Spam ham & eggs'
...
>>> class FooProperty(FooExpensive):
... _cached_bar = None
... @property
... def bar(self):
... if not self._cached_bar:
... self._cached_bar = self._get_expensive_bar_expression()
... return self._cached_bar
...
>>> f = FooProperty()
>>> f.bar
Doing something expensive
'Spam ham & eggs'
>>> f.bar
'Spam ham & eggs'
>>> vars(f)
{'_cached_bar': 'Spam ham & eggs'}
>>> class FooDescriptor(FooExpensive):
... bar = CachedProperty(FooExpensive._get_expensive_bar_expression, 'bar')
...
>>> f = FooDescriptor()
>>> f.bar
Doing something expensive
'Spam ham & eggs'
>>> f.bar
'Spam ham & eggs'
>>> vars(f)
{'bar': 'Spam ham & eggs'}
>>> class FooGetAttr(FooExpensive):
... def __getattr__(self, name):
... if name == 'bar':
... bar = self.bar = self._get_expensive_bar_expression()
... return bar
... return super(Foo, self).__getatt__(name)
...
>>> f = FooGetAttr()
>>> f.bar
Doing something expensive
'Spam ham & eggs'
>>> f.bar
'Spam ham & eggs'
>>> vars(f)
{'bar': 'Spam ham & eggs'}
można to zrobić automatycznie z deskryptorów: http://jeetworks.org/node/62 – schlamar
Werkzeug ma lepszego wdrażania z rozległymi komentarze: https://github.com/mitsuhiko/werkzeug/blob/10b4b8b6918a83712170fdaabd3ec61cf07f23ff/werkzeug/utils.py#L35 – schlamar
Zobacz także: [Python lazy property decorator] (http://stackoverflow.com/questions/3012421/python- leniwe-własności-dekorator). – detly