Chắc chắn, chỉ có tài sản của bạn thiết lập thuộc tính một trường hợp mà được trả lại về tiếp cận sau:
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
Bộ mô tả property
là một bộ mô tả dữ liệu (nó thực hiện __get__
, __set__
và __delete__
móc mô tả), vì vậy nó sẽ được gọi ngay cả khi thuộc tính bar
tồn tại trên cá thể, với kết quả cuối cùng là Python bỏ qua thuộc tính đó, do đó cần phải kiểm tra riêng biệt người gác cửa ute trên mỗi truy cập.
Bạn có thể viết mô tả của riêng bạn mà chỉ thực hiện __get__
, tại thời điểm đó Python sử dụng một thuộc tính trên dụ trên mô tả nếu nó tồn tại:
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()
Nếu bạn thích một cách tiếp cận __getattr__
(trong đó có cái gì đó để nói cho nó), mà muốn được:
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)
truy cập tiếp theo sẽ tìm thấy bar
thuộc tính trên các ví dụ và __getattr__
sẽ không được tham khảo ý kiến.
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'}
Bạn có thể làm điều đó tự động với mô tả: http://jeetworks.org/node/62 – schlamar
Werkzeug có thực hiện tốt hơn với mở rộng nhận xét: https://github.com/mitsuhiko/werkzeug/blob/10b4b8b6918a83712170fdaabd3ec61cf07f23ff/werkzeug/utils.py#L35 – schlamar
Xem thêm: [Python lazy property decorator] (http://stackoverflow.com/questions/3012421/python- lazy-property-decorator). – detly