本文讲解了 Python 的 property 特性,即一种符合 Python 哲学地设置 getter 和 setter 的方式。

Python 有一个概念叫做 property,它能让你在 Python 的面向对象编程中轻松不少。在了解它之前,我们先看一下为什么 property 会被提出。

一个简单的例子

比如说你要创建一个温度的类Celsius,它能存储摄氏度,也能转换为华氏度。即:

class Celsius:
def __init__(self, temperature = 0):
self.temperature = temperature def to_fahrenheit(self):
return (self.temperature * 1.8) + 32

我们可以使用这个类:

>>> # 创建对象 man
>>> man = Celsius() >>> # 设置温度
>>> man.temperature = 37 >>> # 获取温度
>>> man.temperature
37 >>> # 获取华氏度
>>> man.to_fahrenheit()
98.60000000000001

最后额外的小数部分是浮点误差,属于正常现象,你可以在 Python 里试一下 1.1 + 2.2

在 Python 里,当我们对一个对象的属性进行赋值或估值时(如上面的temperature),Python 实际上是在这个对象的 __dict__字典里搜索这个属性来操作。

>>> man.__dict__
{'temperature': 37}

因此,man.temperature实际上被转换成了man.__dict__['temperature']

假设我们这个类被程序员广泛的应用了,他们在数以千计的客户端代码里使用了我们的类,你很高兴。

突然有一天,有个人跑过来说,温度不可能低于零下273度,这个类应该加上对温度的限制。这个建议当然应该被采纳。作为一名经验丰富的程序员,你立刻想到应该使用 setter 和 getter 来限制温度,于是你将代码改成下面这样:

class Celsius:
def __init__(self, temperature = 0):
self.set_temperature(temperature) def to_fahrenheit(self):
return (self.get_temperature() * 1.8) + 32 # 更新部分
def get_temperature(self):
return self._temperature def set_temperature(self, value):
if value < -273:
raise ValueError("Temperature below -273 is not possible")
self._temperature = value

很自然地,你使用了“私有变量”_temperature来存储温度,使用get_temperature()set_temperature()提供了访问_temperature的接口,在这个过程中对温度值进行条件判断,防止它超过限制。这都很好。

问题是,这样一来,使用你的类的程序员们需要把他们的代码中无数个obj.temperature = val改为obj.set_temperature(val),把obj.temperature改为obj.get_temperature()。这种重构实在令人头痛。

所以,这种方法不是“向下兼容”的,我们要另辟蹊径。

@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)

我们在class Celsius的最后一行使用了一个 Python 内置函数(类) property()。它接受两个函数作为参数,一个 getter,一个 setter,并且返回一个 property 对象(这里是temperature)。

这样以后,任何访问temperature的代码都会自动转而运行get_temperature(),任何对temperature赋值的代码都会自动转而运行set_temperature()我们在代码里加了print()便于测试它们的运行状态。

>>> c = Celsius()  # 此时会运行 setter,因为 __init__ 里对 temperature 进行了赋值
Setting value >>> c.temperature # 此时会运行 getter,因为对 temperature 进行了访问
Getting value
0

需要注意的是,实际的温度存储在_temperature里,temperature只是提供一个访问的接口。

深入了解 Property

正如之前提到的,property()是 Python 的一个内置函数,同时它也是一个类。函数签名为:

property(fget=None, fset=None, fdel=None, doc=None)

其中,fget是一个 getter 函数,fset是一个 setter 函数,fdel是删除该属性的函数,doc是一个字符串,用作注释。函数返回一个 property 对象。

一个 property 对象有 getter()setter()deleter()三个方法用来指定相应绑定的函数。之前的

temperature = property(get_temperature,set_temperature)

实际上等价于

# 创建一个空的 property 对象
temperature = property()
# 绑定 getter
temperature = temperature.getter(get_temperature)
# 绑定 setter
temperature = temperature.setter(set_temperature)

这两个代码块等价。

熟悉 Python 装饰器的程序员肯定已经想到,上面的 property 可以用装饰器来实现。

通过装饰器@property,我们可以不定义没有必要的 get_temperature()set_temperature(),这样还避免了污染命名空间。使用方式如下:

class Celsius:
def __init__(self, temperature = 0):
self._temperature = temperature def to_fahrenheit(self):
return (self.temperature * 1.8) + 32 # Getter 装饰器
@property
def temperature(self):
print("Getting value")
return self._temperature # Setter 装饰器
@temperature.setter
def temperature(self, value):
if value < -273:
raise ValueError("Temperature below -273 is not possible")
print("Setting value")
self._temperature = value

你可以使用装饰器,也可以使用之前的方法,完全看个人喜好。但使用装饰器应该是更加 Pythonic 的方法吧。

参考

Python @property

(本文完)

Python @property 详解的更多相关文章

  1. python property详解

    Python中有一个被称为属性函数(property)的小概念,它可以做一些有用的事情.在这篇文章中,我们将看到如何能做以下几点: 将类方法转换为只读属性 重新实现一个属性的setter和getter ...

  2. 第7.27节 Python案例详解: @property装饰器定义属性访问方法getter、setter、deleter

    上节详细介绍了利用@property装饰器定义属性的语法,本节通过具体案例来进一步说明. 一.    案例说明 本节的案例是定义Rectangle(长方形)类,为了说明问题,除构造函数外,其他方法都只 ...

  3. 第7.25节 Python案例详解:使用property函数定义与实例变量同名的属性会怎样?

    第7.25节 Python案例详解:使用property函数定义与实例变量同名的属性会怎样? 一.    案例说明 我们上节提到了,使用property函数定义的属性不要与类内已经定义的普通实例变量重 ...

  4. 第7.24节 Python案例详解:使用property函数定义属性简化属性访问代码实现

    第7.24节 Python案例详解:使用property函数定义属性简化属性访问代码实现 一.    案例说明 本节将通过一个案例介绍怎么使用property定义快捷的属性访问.案例中使用Rectan ...

  5. Python Collections详解

    Python Collections详解 collections模块在内置数据结构(list.tuple.dict.set)的基础上,提供了几个额外的数据结构:ChainMap.Counter.deq ...

  6. Objective-C中的@Property详解

    Objective-C中的@Property详解 @Property (属性) class vairs 这个属性有nonatomic, strong, weak, retain, copy等等 我把它 ...

  7. Python闭包详解

    Python闭包详解 1 快速预览 以下是一段简单的闭包代码示例: def foo(): m=3 n=5 def bar(): a=4 return m+n+a return bar >> ...

  8. [转] Python Traceback详解

    追莫名其妙的bugs利器-mark- 转自:https://www.jianshu.com/p/a8cb5375171a   Python Traceback详解   刚接触Python的时候,简单的 ...

  9. python 数据类型详解

    python数据类型详解 参考网址:http://www.cnblogs.com/linjiqin/p/3608541.html 目录1.字符串2.布尔类型3.整数4.浮点数5.数字6.列表7.元组8 ...

随机推荐

  1. 洛谷P3480 KAM-Pebbles

    题目大意: 有N堆石子,除了第一堆外,每堆石子个数都不少于前一堆的石子个数.两人轮流操作每次操作可以从一堆石子中移走任意多石子,但是要保证操作后仍然满足初始时的条件.谁没有石子可移时输掉游戏.问先手是 ...

  2. plink计算两个SNP位点的连锁不平衡值(LD)

    PLINK提供了“--ld”的参数计算两个SNP位点的连锁不平衡值. 命令如下: plink --file file --ld rs123 rs134 --out rs123_rs134 生成如下数据 ...

  3. org.apache.catalina.core.StandardContext.startInternal Context [/test] startup failed due to previou

    解决方法: WEB-INF/classes目录下新建一个文件叫logging.properties,截图如下: 代码如下: handlers=org.apache.juli.FileHandler,j ...

  4. codeforces-1144 (div3)

    赛后经验:div3过于简单,以后不做了 A.存在以下情况即为NO 1.存在相同字母 2.最大字母-最小字母 != 字符串长度 #include <map> #include <set ...

  5. java 打印空心菱形的两种实现

    第一种实现方式: //打印给定行数的空心菱形 public static void draw(int size){ if (size % 2 == 0) //如果是偶数行变为奇数 { size++; ...

  6. python 高级部分

    伴随视频可以观看 因为Python的线程虽然是真正的线程,但解释器执行代码时,有一个GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁,然后,每执 ...

  7. IntelliJ IDEA 2018最新版注册码激活方法

    一.首先点击intellij idea 2018 二.选择激活码 三.输入以下激活码intellij idea 2018 最新版本 注册激活码 **************************** ...

  8. Mountaineers Gym - 102021M (LCA+MST)

    题目链接: Mountaineers  Gym - 102021M 题目大意:给你一个n*m的矩阵,a[i][j]代表当前方块的高度,然后每次询问给你一个起点和终点,然后问你在这个图上你选择一条路径, ...

  9. TensorFlow在Windows上的CPU版本和GPU版本的安装指南(亲测有效)

    安装说明 平台:Window.Ubuntu.Mac等操作系统 版本:支持GPU版本和CPU版本 安装方式:pip方式.Anaconda方式 attention: 在Windows上目前支持python ...

  10. [系统集成] 基于 elasticsearch 的企业监控方案

    注: 2017年10月16日: 使用中发现 es 查询时序数据的性能较差,且 watch 脚本的编写比较麻烦,因此已将监控系统切换到了 influxdb+grafana平台.新监控系统各方面情况比较满 ...