Python中的@property特性

你将了解Python中的 @property特性, 以pythonic的方式使用getter和setter。

目录表

从一个例子开始

使用 Getter 和 Setter

@property的力量

深入理解Property

Python有一个很棒的概念,叫做property,它使面向对象的程序员的生活更加简单。

在定义和研究@property是什么之前,让我们先直观感受下为什么首先应该使用它。

从一个例子开始

我们假设你决定创建一个可以以摄氏度存储温度的类[1],它还将实现一个将温度转换为华氏度的方法。一种方法是像下面这样做。

我们可以使用这个类创建对象,并按照我们的意愿操作其属性temperature。你可以在Python shell上试试这些操作。

在转换成华氏温度时,由于浮点运算错误(请在Python解释器中尝试1.1 + 2.2),小数点多了一位。

当我们赋值或检索任何对象属性像temperature时,如上面所示,Python都会在该对象的__dict__字典中搜索它。

因此,man.temperature在内部会变成man.__dict__['temperature']。

现在,让我们进一步假设我们的类在客户中很受欢迎,他们开始在自己的程序中使用它。他们对这个对象做了各种各样的赋值操作。

有一天,一个值得信赖的客户来找我们,建议温度不能低于-273摄氏度(热力学专业的学生可能会说实际上是-273.15摄氏度),也被称为绝对零度。他还要求我们实现这个值约束。作为一家追求客户满意的公司,我们很高兴地采纳了这个建议,并发布了1.01版本(对现有的类进行升级)。

使用Getter和Setter

上述对值进行约束的一个明显解决方案是隐藏属性temperature(使其私有)并定义新的 getter 和 setter 接口来操作它。可以按照下面这样做。

从上面可以看出,我们定义了get_temperature()和set_temperature()两个新方法,并且用_temperature替换了temperature。在Python中,开头的下划线 (_) 用于表示私有变量。

这次更新成功地实现了新的限制。我们没法再将温度设置在-273度以下。

请注意,Python中不存在私有变量,但还是有一些简单的准则可以遵循。Python语言本身并不会做限制。

但这并不是什么大问题。上述更新的一个大问题是,所有在其程序中实现我们前面的类的客户都必须修改他们的代码,将obj.temperature修改为obj.get_temperature(),并且将像obj.temperature = val的所有赋值语句修改为obj.set_temperature(val)。

这种重构可能会给客户带来数十多万行代码的麻烦。

总之,我们的新更新不向后兼容。这时property就派上用场了。

@property的力量

处理上述问题的Pythonic的方法是使用property。以下是我们如何实现它。

然后,在shell中运行以下代码并注意观察。

我们在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对象,它为这个私有变量提供接口。

深入理解 Property

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

其中,fget是获取属性值的函数,fset是设置属性值的函数,fdel是删除属性的函数,doc是字符串(像注释一样)。从上述实现中可以看出,这些函数参数是可选的。因此,可以按照以下方式创建property对象。

property对象有三个方法,getter()、setter()和deleter(),用于后续指定fget、fset和fdel。这意味着本行代码

可以分解为

这两段代码是等价的。

熟悉Python中的装饰器[2]的程序员可能会意识到,上面的构造可以实现为装饰器。

我们可以继续,不去定义get_temperature和set_temperature名称,因为它们是不必要的,并且会污染类命名空间。为此,我们在定义getter和setter函数时重用了名称temperature。这就是它实现的方式。

上述实现是既简单又推荐使用的创建property的方法。当你在Python中寻找property时,你很可能会遇到这些类型的构造。

好了,今天就到这里。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20190221A06FSI00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券