Python 中的 property 属性

在详细解释和深入了解Python中的property之前,让我们首先建立这样一个直觉:为什么我们需要用到property?

从一个实例开始

假设有天你决定创建一个类,用来存储摄氏温度。当然这个类也需要实现一个将摄氏温度转换为华氏温度的方法。一种实现的方式如下:

现在,让我们进一步假设我们的类在客户中很受欢迎,他们开始在其程序中使用这个类。他们对该类生成的对象做了各种操作。

有一天,一个受信任的客户来找我们,建议温度不能低于-273摄氏度(热力学的同学可能会提出异议,它实际上是-273.15),也被称为绝对零。

客户进一步要求我们实现这个值约束。作为一个以争取客户满意度为己任的公司,我们很高兴地听从了建议,发布了1.01版本,升级了我们现有的类。

使用Getters和Setters

对于上边的约束,一个很容易想到的解决方案是隐藏其温度属性(使其私有化),并且定义新的用于操作温度属性的getter和setter接口。可以这么实现:

请注意,Python中实际上是没有私有变量的。有一些简单的被遵循的规范。Python本身不会应用任何限制

 

 

但这样并不会让人很放心。上述更新的最大问题是,所有在他们的程序中使用了我们先前类的客户都必须更改他们的代码:obj.temperature改为obj.get_temperature(),

 

所有的赋值语句也必须更改,比如obj.temperature = val改为obj.set_temperature(val)。这样的重构会给那些拥有成千上万行代码的客户带来很大的麻烦。

总而言之,我们的更新是不向后兼容地。这就是需要property闪亮登场的地方。

Property的作用

对于上边的问题,Python式的解决方式是使用property。这里是我们已经实现了的一个版本:

class Celsius:
    def __init__(self, temperature = 0):
        self.temperature = temperature
 
    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32
 
    def get_temperature(self):
        print("Getting value")
        return self._temperature
 
    def set_temperature(self, value):
        if value < -273:
            raise ValueError("Temperature below -273 is not possible")
        print("Setting value")
        self._temperature = value
 
    temperature = property(get_temperature,set_temperature)

我们在get_temperature()set_temperature()的内部增加了一个print()函数,用来清楚地观察它们是否正在执行。

代码的最后一行,创建了一个property对象temperature。简单地说,property将一些代码(get_temperatureset_temperature)附加到成员属性(temperature)的访问入口。

任何获取temperature值的代码都会自动调用get_temperature(),而不是去字典表(__dict__)中进行查找。

同样的,任何赋给temperature值的代码也会自动调用set_temperature()。这是Python中一个很酷的功能。我们实际演示一下。

最后需要注意的是,实际温度值存储在私有变量_temperature中。属性temperature是一个property对象,是用来为这个私有变量提供接口的。

深入挖掘property

在Python中,property()是一个内置函数,用于创建和返回一个property对象。该函数的签名为:

熟悉Python中装饰器decorator的程序员能够认识到上述结构可以作为decorator实现。我们可以更进一步,不去定义名字get_temperature和set_temperature,

因为他们不是必须的,并且污染类的命名空间。为此,我们在定义getter函数和setter函数时重用名字temperature。下边的代码展示如何实现它:

posted on 2018-02-01 18:01  三井寿  阅读(207)  评论(0编辑  收藏  举报