Laurens Koppenol建议使用properties
(Python对计算属性的一般支持),这是一个好主意,但他的示例代码在许多方面都被破坏并且比它更复杂,所以这里有一个更简单的工作和pythonic示例(无Updatable
类,也没有任何其他多余的东西需要):
class Rectangle(object):
def __init__(self, length, perimeter):
self.length = length
self.perimeter = perimeter
@property
def width(self):
return 0.5*(self.perimeter - 2.*self.length)
如果你想缓存width
值(以避免无用计算),但仍使当length
或perimeter
变化,你需要确保它的更新使他们全部属性:
class Rectangle(object):
def __init__(self, length, perimeter):
self.length = length
self.perimeter = perimeter
@property
def length(self):
return self._length
@length.setter
def length(self, value):
self._length = value
self._width = None
@property
def perimeter(self):
return self._perimeter
@length.setter
def perimiter(self, value):
self._perimeter = value
self._width = None
@property
def width(self):
if self._width is None:
self._width = 0.5*(self.perimeter - 2.*self.length)
return self._width
或者(如果你有很多这样的东西)使用一些“cached_property与失效”的实施,因为这一个:Storing calculated values in an object
编辑:WRT /你的问题,调用locals
的确是丑(和可能很容易中断 - 你可能有本地变量,不应该是_vars
的一部分),以及需要在子类中明确设置self._vars
。另外update()
API本身相当丑陋恕我直言。现在你不需要任何幻想,使整个事情更Python - 这里有一个解决方案的唯一样板是需要调用Updateable.__init__
与命名的参数(不带位置的人的工作):
class Updateable(object):
def __init__(self, **kwargs):
self._vars = kwargs
def update(self, **kwargs):
vars = self._vars.copy()
vars.update(**kwargs)
self.__init__(**vars)
class Rectangle(Updateable):
def __init__(self, length, perimeter):
super(Rectangle, self).__init__(length=length, perimeter=perimeter)
self.length = length
self.width = 0.5*(perimeter - 2.*length)
r = Rectangle(10, 20)
r.update(perimeter=40)
作为侧面说明,我personnaly发现很令人不安,你的Rectangle
类需要perimeter
论点,但存储width
而不是...也许你应该考虑perimeter
属性? (即使只读,以避免重新计算等)
对象已存在后调用__init__似乎有点奇怪。为什么不只是一个'recalculate_width'方法呢? – BrenBarn
因为我希望它适用于我有的其他类,它们都有不同的参数名称。有时它是'宽度',有时它是'角度',有时它是'波长',可能是任何东西。 –