动态更新依赖于同一对象的其他属性状态的对象的属性

时间:2014-12-19 18:07:18

标签: python

假设我有一个看起来像这样的课程:

class Test(object):
   def __init__(self, a, b):
      self.a = a
      self.b = b
      self.c = self.a + self.b 

我希望只要属性self.cself.a的值在同一个实例中发生更改,self.b的值就会发生变化。

e.g。

test1 = Test(2,4)
print test1.c # prints 6

test1.a = 3
print test1.c # prints = 6 

我知道为什么它仍会打印6,但是当self.c发生变化时,我可以使用一种机制来发布self.a的更新。或者我唯一的选择是根据当前状态self.cself.a

的方法返回self.b的值

2 个答案:

答案 0 :(得分:19)

是的,有!它被称为属性。

只读属性

class Test(object):
    def __init__(self,a,b):
        self.a = a
        self.b = b
    @property
    def c(self):
        return self.a + self.b

使用上面的代码,c现在是Test类的只读属性。

可变属性

您还可以为属性提供 setter ,这将使其可读/写并允许您直接设置其值。它看起来像这样:

class Test(object):
    def __init__(self, c = SomeDefaultValue):
        self._c = SomeDefaultValue
    @property
    def c(self):
        return self._c
    @c.setter
    def c(self,value):
        self._c = value

但是,在这种情况下,为self.c设置一个setter是没有意义的,因为它的值取决于self.aself.b

@property是什么意思?

@property位是一个名为装饰器的例子。装饰器实际上包装它装饰到另一个函数(装饰器函数)的函数(或类)。在函数被装饰之后,当它被调用时,它实际上是用函数(及其参数)作为参数调用的装饰器。通常(但不总是!)装饰函数做一些有趣的事情,然后像通常那样调用原始(装饰)函数。例如:

def my_decorator(thedecoratedfunction):
    def wrapped(*allofthearguments):
        print("This function has been decorated!") #something interesting
        thedecoratedfunction(*allofthearguments)   #calls the function as normal
    return wrapped

@my_decorator
def myfunction(arg1, arg2):
    pass

这相当于:

def myfunction(arg1, arg2):
    pass
myfunction = my_decorator(myfunction)

所以这意味着在上面的类示例中,您也可以这样做,而不是使用装饰器:

def c(self):
    return self.a + self.b
c = property(c)

他们完全是一回事。 @property只是用语法getter和setter替换对myobject.c的调用的语法糖(删除器也是一个选项)。

等等 - 这是如何工作的?

您可能想知道为什么只需执行一次

myfunction = my_decorator(myfunction)

...导致如此剧烈的变化!所以,从现在开始,在致电:

myfunction(arg1, arg2)

...您实际上正在呼叫my_decorator(myfunction)arg1, arg2已发送到wrapped内部的my_decorator内部功能。不仅如此,所有这一切都发生在,即使你甚至没有在函数调用中提到my_decoratorwrapped

所有这些都是通过一种叫做闭包的东西来实现的。当函数以这种方式传递给装饰器时(例如,property(c)),函数的名称被重新绑定到函数的包装版本而不是原始函数和原始函数&# 39; s参数总是传递给wrapped而不是原始函数。这只是闭包的工作方式,并且没有任何神奇之处。 Here is some more information about closures

描述符

所以总结到目前为止:@property只是将类方法包装在property()函数内部的一种方式,因此调用包装的类方法而不是原始的,未包装的类方法。 但是属性功能是什么? 它做了什么?

属性函数向类中添加了一个名为 descriptor 的东西。简而言之,描述符是一个对象类,可以有单独的get,set和delete方法。当你这样做时:

@property
def c(self):
    return self._c

...您正在向名为Test的{​​{1}}类添加一个描述符,并定义{em> get 方法(实际上,c){ {1}}描述符等于__get__()方法。

执行此操作时:

c

...您正在将c(self)描述符的 set 方法(实际上,@c.setter def c(self,value): self._c )定义为等于__set__()方法。

摘要

只需在c方法中添加c(self,value)即可完成大量内容!在实践中,您可能不需要立即了解所有这些以开始使用它。但是,我建议请记住,当您使用@property时,您正在使用装饰器,闭包和描述符,如果您非常认真地学习Python,那么值得您花时间研究这些主题他们自己。

答案 1 :(得分:6)

最简单的解决方案是将c设为只读property

class Test(object):

    def __init__(self, a, b):
        self.a = a
        self.b = b

    @property
    def c(self):
        return self.a + self.b

现在每次访问test_instance.c时,它都会调用属性getter并从其他属性中计算出适当的值。使用中:

>>> t = Test(2, 4)
>>> t.c
6
>>> t.a = 3
>>> t.c
7

请注意,这意味着您无法直接设置c

>>> t.c = 6

Traceback (most recent call last):
  File "<pyshell#16>", line 1, in <module>
    t.c = 6
AttributeError: can't set attribute