对于上边的问题,Python式的解决方式是使用property。这里是我们已经实现了的一个版本
我们在get_temperature()
和set_temperature()
的内部增加了一个print()函数,用来清楚地观察它们是否正在执行。代码的最后一行,创建了一个property对象temperature。简单地说,property将一些代码(get_temperature
和set_temperature
)附加到成员属性(temperature)的访问入口。任何获取temperature值的代码都会自动调用get_temperature()
,而不是去字典表(__dict__
)中进行查找。同样的,任何赋给temperature值的代码也会自动调用set_temperature()
。这是Python中一个很酷的功能。我们实际演示一下。
从上边的代码中我们可以看到,即使当我们创建一个对象时,set_temperature()
也会被调用。你能猜到为什么吗?原因是,当一个对象被创建时,__init__()
方法被调用。该方法有一行代码self.temperature = temperature。这个任务会自动调用set_temperature()
方法。
同样的,对于属性的任何访问,例如c.temperature,也会自动调用get_temperature()
方法。这就是property所作的事情。这里有一些额外的实例。
我们可以看到,通过使用property,我们在不需要客户代码做任何修改的情况下,修改了我们的类,并实现了值约束。因此我们的实现是向后兼容的,这样的结果,大家都很高兴。
最后需要注意的是,实际温度值存储在私有变量_temperature
中。属性temperature是一个property对象,是用来为这个私有变量提供接口的。
在Python中,property()是一个内置函数,用于创建和返回一个property对象。该函数的签名为:
这里,fget是一个获取属性值的函数,fset是一个设置属性值的函数,fdel是一个删除属性的函数,doc是一个字符串(类似于注释)。从函数实现上看,这些函数参数都是可选的。所以,可以按照如下的方式简单的创建一个property对象。
Property对象有三个方法,getter(), setter()和delete(),用来在对象创建后设置fget,fset和fdel。这就意味着,这行代码:temperature = property(get_temperature,set_temperature)可以被分解为:
它们之间是相互等价的。
熟悉Python中装饰器decorator的程序员能够认识到上述结构可以作为decorator实现。我们可以更进一步,不去定义名字get_temperature和set_temperature,因为他们不是必须的,并且污染类的命名空间。为此,我们在定义getter函数和setter函数时重用名字temperature。下边的代码展示如何实现它。
上边的两种生成property的实现方式,都很简单,推荐使用。在Python寻找property时,你很可能会遇到这种类似的代码结构。