问题描述
我有一个自定义类,其实例用几个属性foo,bar,...
初始化,这些属性是数字值或字符串。几种类方法使用这些属性来计算类实例的其他属性。我想将这些属性定义为属性,以便在更改它们时会重新计算其他属性。以下作品:
class MyClass:
def __init__(self,foo,bar):
self._foo = foo
self._bar = bar
@property
def foo(self):
return self._foo
@foo.setter
def foo(self,new_foo):
old_foo = self._foo:
self._foo = new_foo
if old_foo != new_foo:
self._force_recalc()
@property
def bar(self):
return self._bar
@bar.setter
def bar(self,new_bar):
old_bar = self._bar:
self._bar = new_bar
if old_bar != new_bar:
self._force_recalc()
def _force_recalc(self)
# call some methods that recalculate attributes
但是,有两个方面让我感到震惊:
- setter方法基本上是复制粘贴,只调整了变量名。但是我无法提出一种使用通用设置方法的解决方案。
- 是否有避免
old_foo
和old_bar
的方法?它们是由于_force_recalc
必须在foo
和bar
重设之后被调用,但前提是它们的值必须更改。
解决方法
我首先尝试了@Barmar建议的解决方案。这是可行的,但是会导致在初始化所有属性期间出现不必要的重新计算量的问题。另外,它们仍然必须单独定义,我无法检查该值是否实际更改。
按照@ juanpa.arrivillaga的建议,我定义了自己的装饰器类,最终得到了这样的内容:
class MyClass:
class _MyProperty:
def __init__(self,value=None):
self.value = value
def __get__(self,instance,owner=None):
return self.value
def __set__(self,value):
if value != self.value:
self.value = value
instance._force_recalc()
@classmethod
def _set_init_state(cls,values):
cls.foo,cls.bar = [cls._MyProperty(value) for value in values]
def __init__(self,foo,bar):
self._set_init_state((foo,bar))
self._force_recalc()
def _force_recalc(self)
# call some methods that recalculate attributes
虽然这看起来似乎不太重复,但可以说前者更为明确。但是,我喜欢它,它可以解决我的原始要求。