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 with invalidation”实现:Storing calculated values in an object
编辑:wrt/您的问题,对locals 的调用确实很难看(并且很容易中断-您可能有不应该是_vars 一部分的局部变量),以及需要明确在子类中设置self._vars。恕我直言,update() API 本身也很丑陋。现在你不需要任何花哨的东西来让整个事情变得更加 Pythonic - 这是一个解决方案,其唯一的样板是需要使用命名参数调用 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)
作为旁注,我个人觉得您的 Rectangle 类采用 perimeter 参数但存储 width 而不是......也许您应该考虑使用 perimeter 属性非常令人不安? (即使是只读以避免重新计算等)