我经常遇到这种情况,并没有设法找到一种稳定的方法来处理它。
假设我有一个类定义的类:
class MyClass(object):
def __init__(self, a, b):
self.a = a
self.b = b
@property
def c(self):
"""This method performs some heavy computations based on a and b properties"""
# Some heavy computations only with a and b attributes
return c
现在可以通过以下方式检索属性c
:
>>> obj = MyClass(a, b)
>>> print obj.c
但是,每次我要求obj.c
时,都会执行繁重的计算,导致性能代码不佳,因为c
会导致繁重的计算,并且最好只在{{1设置或修改{}或a
。
处理此案件的更好方法是什么?我正在考虑创建一个b
方法,以用作某些c_update
和@a.setter
装饰方法的装饰器,但这是更好的方法吗?
此致
答案 0 :(得分:1)
但是如果我有很多依赖于a和b值的XX依赖属性呢?我是否必须为每个方法编写update_XX方法,并将此方法添加到init以及每个a.setter和b.setter?这在我看来相当冗长......
每当c
或a
发生变异时,您都可以更新b
值(以及任何其他数量的相关属性),我在下面实施了update_properties()
方法:
class MyClass(object):
def __init__(self, a, b):
self._a = a
self._b = b
self.update_properties()
@property
def a(self):
return self.a
@a.setter
def a(self, value):
self._a = value
self.update_properties()
@property
def b(self):
return self._b
@b.setter
def b(self, value):
self._b = value
self.update_properties()
def update_properties(self):
self.c = self._a + self._b
self.d = self._a * self._b
self.e = self._a - self._b
# self.f = ...
# ...
# self.z = ...
# Can go on as long as you want
你认为有可能将这个机制作为一些装饰器来实现,以减轻代码
详细程度似乎仅在跟踪自由变量的一侧(例如此处a
和b
),所以如果我必须支持任意数量的那些,我会实现MyClass.set_value(name, value)
def set_value(self, name, value):
setattr(self, name, value)
self.update_properties()
所以这里的想法是我们的set_value()
可以使用任意数量的属性。如果您使用__init__
解压缩传递给构造函数的键值,则可以从**kwargs
调用它。
此处有一项要求,因为我们未将自由变量设置为@property
我们需要使用obj.set_value('a', 42)
代替obj.a = 42
答案 1 :(得分:0)
我只是将c
的实际值存储在私有属性中,并检查这不是None
。当None
或a
发生变化时,请将此设置为b
。
因此,使用属性执行此操作的“正确”方法是:
class MyClass(object):
def __init__(self, a, b):
self._a = a
self._b = b
self._c = None
@property
def a(self):
return self._a
@a.setter
def a(self, value):
self._a = value
self._c = None
@property
def b(self):
return self._b
@b.setter
def a(self, value):
self._b = value
self._c = None
@property
def c(self):
if self._c is None:
self._c = # compute c here
return self._c
如果您想避免创建所有这些属性和设置器,您可能想要劫持__getattr__
和__setattr__
方法:
class MyClass(object):
def __init__(self, a, b):
self.a = a
self.b = b
self._c = None
def __getattr__(self, name):
if name == 'c':
if self._c is None:
self._c = # compute c here
return self._c
raise AttributeError(name)
def __setattr__(self, name, value):
if name == 'c':
raise TypeError('You cannot modify property c directly')
super(MyClass, self).__setattr__(name, value)
if name in ('a', 'b'):
super(MyClass, self).__setattr__('_c', None)
请注意,最后一个解决方案可以扩展为10个属性a1
,...,a10
,而无需定义10个属性和设置器。
它可能不太健壮。
答案 2 :(得分:0)
因此,根据您的答案,我设法使用dict为依赖属性构建一个新答案。
class MyClass(object):
def __init__(self, a, b):
self._properties = dict()
self._a = a
self._b = b
def _update_dependent_properties(self):
# Do computations for c1, c2...
self._properties['c1'] = # c1 value
self._properties['c2'] = # c2 value
# ...
@property
def a(self):
return self._a
@property
def b(self):
return self._b
@a.setter
def a(self, value):
self._properties.clean()
self._a = value
@b.setter
def b(self, value):
self._properties.clean()
self._b = value
@property
def c1(self):
try:
return self._properties['c1']
except KeyError:
_update_dependent_properties()
return self._properties['c1']
@property
def c2(self):
try:
return self._properties['c2']
except KeyError:
_update_dependent_properties()
return self._properties['c2']
这似乎可以解决这个问题,但它仍然非常冗长......我仍然要为我期望的每个依赖属性编写一个属性。但是,当update_dependent_properties()
或a
属性被修改时,它会强制计算b
。
我想知道它是否不存在这样做的模块。似乎我的问题听起来像memoize技术......并且装饰者可以通过系统化程序来减轻代码吗?
答案 3 :(得分:0)
有一个小的pypi包非常适合:cached-property
from cached_property import cached_property
class MyClass(object):
def __init__(self):
pass
@cached_property
def c(self):
# Heavy computation based on self.a / self.b
return ...
@property
def a(self):
return self._a
@a.setter
def a(self, value):
self._a = value
del self.c
@property
def b(self):
return self._b
@b.setter
def b(self, value):
self._b = value
del self.c
当然,您还可以为a
/ b
属性构建抽象,利用del self.c
。
使用cached_property
的一个好处是,您可以通过将缓存更改为threaded_cached_property
轻松地使缓存线程安全。