我很好奇哪种更好的模式来创建属性属性,以在第一次使用时初始化其值。下面是一个在主题上有多种变体的课程。
def some_initializer(s):
return f"Value: {s}"
class Foo(object):
""" Initialize on first use properties """
def __init__(self):
self._prop1 = None
@property
def prop1(self):
""" Existing private member attribute """
if not self._prop1:
self._prop1 = some_initializer("prop1")
return self._prop1
@property
def prop2(self):
""" Create private attribute on demand """
if not hasattr(self, "_prop2"):
self._prop2 = some_initializer("prop2")
return self._prop2
@property
def prop3(self):
""" Create private attribute on demand - shorter """
self._prop3 = getattr(self, "_prop3", some_initializer("prop3"))
return self._prop3
@property
def prop4(self):
""" Stash value in attribute with same name as property """
_prop4 = self.__dict__.get('_prop4')
if _prop4 is not None:
return _prop4
self._prop4 = _prop4 = some_initializer("prop4")
return _prop4
>> f = Foo()
>> print(f.prop1)
>> print(f.prop2)
>> print(f.prop3)
>> print(f.prop4)
Value: prop1
Value: prop2
Value: prop3
Value: prop4
过去,我使用了prop1,prop2和prop3。最近,我被介绍到prop4变体,虽然在技术上也许是正确的,但我仍然感到困惑。这些变化有什么弊端,或者也许有更好的方法?
编辑:理想情况下,保持属性设置器和删除器装饰器的兼容性也是很好的选择。
答案 0 :(得分:2)
我只写一个自定义描述符,并使用它:
class cached_property:
def __init__(self, f):
self.f = f
def __get__(self, instance, owner):
if not instance:
return self
res = instance.__dict__[self.f.__name__] = self.f(instance)
return res
用法:
class C:
@cached_property
def prop(self):
print('you will see me once')
return 4
答案 1 :(得分:1)
您可以使用functools.lru_cache
来记住属性值:
from functools import lru_cache
class Foo(object):
@property
@lru_cache()
def prop(self):
print("called once")
return 42
foo = Foo()
print(foo.prop)
print(foo.prop)
答案 2 :(得分:0)
我也想到了描述符,但是想出了这种方法
from weakref import WeakKeyDictionary
def five():
return 5
def six():
return 6
def seven():
return 7
class FirstInit:
def __init__(self, initializer):
self.initializer = initializer
self.data = WeakKeyDictionary()
def __get__(self, instance, owner):
try:
value = self.data[instance]
except KeyError as e:
value = self.initializer()
self.data[instance] = value
return self.data[instance]
用法:
class F:
a = FirstInit(five)
b = FirstInit(six)
c = FirstInit(seven)
def __init__(self,name):
self.name = f'{name}:{self.c}'
>>> f = F('foo')
>>> f.name
'foo:7'
>>> f.a, f.b
(5, 6)
>>> f.a = 'sixteen'
>>> f.a, f.b
('sixteen', 6)
>>> f.b += 13
>>> f.a, f.b
('sixteen', 19)
>>>
对于带有参数的初始化器:
d = {'P1':5, 'P2':6, 'P3':7}
def initializer(which):
return d[which]
class FirstInit:
def __init__(self, initializer, prop):
self.initializer = initializer
self.prop = prop
self.data = WeakKeyDictionary()
def __get__(self, instance, owner):
try:
value = self.data[instance]
except KeyError as e:
value = self.initializer(self.prop)
self.data[instance] = value
return self.data[instance]
class G:
a = FirstInit(initializer, 'P1')
b = FirstInit(initializer, 'P2')
c = FirstInit(initializer, 'P3')
def __init__(self,name):
self.name = f'{name}:{self.c}'
...
>>> g = G('foo')
>>> g.name
'foo:7'
>>> g.b += 16
>>> g.a,g.b
(5, 22)
>>> g.a = 'four'
>>> g.a,g.b
('four', 22)
>>>