Python @property 详解
本文讲解了 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详解
Python中有一个被称为属性函数(property)的小概念,它可以做一些有用的事情.在这篇文章中,我们将看到如何能做以下几点: 将类方法转换为只读属性 重新实现一个属性的setter和getter ...
- 第7.27节 Python案例详解: @property装饰器定义属性访问方法getter、setter、deleter
上节详细介绍了利用@property装饰器定义属性的语法,本节通过具体案例来进一步说明. 一. 案例说明 本节的案例是定义Rectangle(长方形)类,为了说明问题,除构造函数外,其他方法都只 ...
- 第7.25节 Python案例详解:使用property函数定义与实例变量同名的属性会怎样?
第7.25节 Python案例详解:使用property函数定义与实例变量同名的属性会怎样? 一. 案例说明 我们上节提到了,使用property函数定义的属性不要与类内已经定义的普通实例变量重 ...
- 第7.24节 Python案例详解:使用property函数定义属性简化属性访问代码实现
第7.24节 Python案例详解:使用property函数定义属性简化属性访问代码实现 一. 案例说明 本节将通过一个案例介绍怎么使用property定义快捷的属性访问.案例中使用Rectan ...
- Python Collections详解
Python Collections详解 collections模块在内置数据结构(list.tuple.dict.set)的基础上,提供了几个额外的数据结构:ChainMap.Counter.deq ...
- Objective-C中的@Property详解
Objective-C中的@Property详解 @Property (属性) class vairs 这个属性有nonatomic, strong, weak, retain, copy等等 我把它 ...
- Python闭包详解
Python闭包详解 1 快速预览 以下是一段简单的闭包代码示例: def foo(): m=3 n=5 def bar(): a=4 return m+n+a return bar >> ...
- [转] Python Traceback详解
追莫名其妙的bugs利器-mark- 转自:https://www.jianshu.com/p/a8cb5375171a Python Traceback详解 刚接触Python的时候,简单的 ...
- python 数据类型详解
python数据类型详解 参考网址:http://www.cnblogs.com/linjiqin/p/3608541.html 目录1.字符串2.布尔类型3.整数4.浮点数5.数字6.列表7.元组8 ...
随机推荐
- 关于Aop切面中的@Before @Around等操作顺序的说明
[转]http://www.cnblogs.com/softidea/p/6123307.html 话不多说,直接上代码: package com.cdms.aop.aspectImpl; impor ...
- Exp6 信息收集与漏洞扫描
一.实践过程 1.信息收集 1.1 通过DNS和IP查询目标网站的信息 (1)whois命令用来进行域名注册信息查询,可查询到3R注册信息,包括注册人的姓名.组织和城市等信息. whois baidu ...
- LeetCode-两数之和
Question 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案.但是,你不能重复利用这 ...
- MySql分页查询慢|这里告诉你答案
一.背景 我们在开发的过程中使用分页是不可避免的,通常情况下我们的做法是使用limit加偏移量:select * from table where column=xxx order by xxx li ...
- Vue打包优化之分析工具webpack-bundle-analyzer
// 1. 安装 cnpm install webpack-bundle-analyzer --save-dev // 2. 在/build/webpack.prod.conf.js文件中引入 con ...
- ConcurrentHashMap扩容
然后,说说精华的部分. Cmap 支持并发扩容,实现方式是,将表拆分,让每个线程处理自己的区间.如下图: 假设总长度是 64 ,每个线程可以分到 16 个桶,各自处理,不会互相影响. 而每个线 ...
- Cmake出现CMake Error: Could not find CMAKE_ROOT !!!
试了很多方式, 其实只需要这一句话!!!!!! hash -r
- 深入理解Java设计模式之观察者模式
前言 观察者,即时observer,总结来说就是一个当一个被观察的对象的状态发生变化时,会通知相应的观察者对象. 观察者模式定义了四种角色:抽象主题.具体主题.抽象观察者.具体观察者 1.抽象主题 ...
- Swift 4 放大镜功能实现
先上效果图吧,框框被限制在了image内了. 这个feature我把它用在了我的app里了,博客写得不容易,来star下啦
- vue父子组件生命周期执行顺序
之前写了vue的生命周期,本以为明白了vue实例在创建到显示在页面上以及销毁等一系列过程,以及各个生命周期的特点.然而今天被问到父子组件生命周期执行顺序的时候一头雾水,根本不知道怎么回事.然后写了一段 ...