Python黑魔法@property装饰器的使用技巧解析


Posted in Python onJune 16, 2016

@property有什么用呢?表面看来,就是将一个方法用属性的方式来访问.
上代码,代码最清晰了.

class Circle(object): 
  def __init__(self, radius): 
    self.radius = radius 
 
  @property 
  def area(self): 
    return 3.14 * self.radius ** 2 
 
c = Circle(4) 
print c.radius 
print c.area

可以看到,area虽然是定义成一个方法的形式,但是加上@property后,可以直接c.area,当成属性访问.
现在问题来了,(不是挖掘机技术哪家强),每次调用c.area,都会计算一次,太浪费cpu了,怎样才能只计算一次呢?这就是lazy property.

class lazy(object): 
  def __init__(self, func): 
    self.func = func 
 
  def __get__(self, instance, cls): 
    val = self.func(instance) 
    setattr(instance, self.func.__name__, val) 
    return val 
 
class Circle(object): 
  def __init__(self, radius): 
    self.radius = radius 
 
  @lazy 
  def area(self): 
    print 'evalute' 
    return 3.14 * self.radius ** 2 
 
c = Circle(4) 
print c.radius 
print c.area 
print c.area 
print c.area

可以看到,'evalute'只输出了一次.如果看了我前面几篇博文,对@lazy的机制应该很好理解.
在这里,lazy类有__get__方法,说明是个描述器,第一次执行c.area的时候,因为顺序问题,先去c.__dict__中找,没找到,就去类空间找,在类Circle中,有area()方法,于是就被__get__拦截.
在__get__中,调用实例的area()方法算出结果,并动态给实例添加个同名属性把结果赋给它,即加到c.__dict__中去.
再次执行c.area的时候,先去c.__dict__找,因为此时已经有了,就不会经过area()方法和__get__了.

注意点
请注意以下代码场景:

代码片段1:
Python2.6代码 

class Parrot(object): 
  def __init__(self): 
    self._voltage = 100000 
 
  @property 
  def voltage(self): 
    """Get the current voltage.""" 
    return self._voltage 
 
if __name__ == "__main__": 
  # instance 
  p = Parrot() 
  # similarly invoke "getter" via @property 
  print p.voltage 
  # update, similarly invoke "setter" 
  p.voltage = 12

代码片段2:
Python2.6代码 

class Parrot: 
  def __init__(self): 
    self._voltage = 100000 
 
  @property 
  def voltage(self): 
    """Get the current voltage.""" 
    return self._voltage 
 
if __name__ == "__main__": 
  # instance 
  p = Parrot() 
  # similarly invoke "getter" via @property 
  print p.voltage 
  # update, similarly invoke "setter" 
  p.voltage = 12

代码1、2的区别在于

class Parrot(object):

在python2.6下,分别运行测试
片段1:将会提示一个预期的错误信息 AttributeError: can't set attribute
片段2:正确运行

参考python2.6文档,@property将提供一个ready-only property,以上代码没有提供对应的@voltage.setter,按理说片段2代码将提示运行错误,在python2.6文档中,我们可以找到以下信息:

BIF:
property([fget[, fset[, fdel[, doc]]]])
Return a property attribute for new-style classes (classes that derive from object).
原来在python2.6下,内置类型 object 并不是默认的基类,如果在定义类时,没有明确说明的话(代码片段2),我们定义的Parrot(代码片段2)将不会继承object

而object类正好提供了我们需要的@property功能,在文档中我们可以查到如下信息:

new-style class
Any class which inherits from object. This includes all built-in types like list and dict. Only new-style classes can use Python's newer, versatile features like __slots__, descriptors, properties, and __getattribute__().

同时我们也可以通过以下方法来验证
Python 2.6代码 

class A: 
  pass 

>>type(A) 
<type 'classobj'>

Python 2.6代码 

class A(object): 
  pass 

>>type(A) 
<type 'type'>

从返回的<type 'classobj'>,<type 'type'>可以看出<type 'type'>是我们需要的object类型(python 3.0 将object类作为默认基类,所以都将返回<type 'type'>)

为了考虑代码的python 版本过渡期的兼容性问题,我觉得应该定义class文件的时候,都应该显式定义object,做为一个好习惯

最后的代码将如下:

class Parrot(object): 
  def __init__(self): 
    self._voltage = 100000 

  @property 
  def voltage(self): 
    """Get the current voltage.""" 
    return self._voltage 

  @voltage.setter 
  def voltage(self, new_value): 
    self._voltage = new_value 
 
if __name__ == "__main__": 
  # instance 
  p = Parrot() 
  # similarly invoke "getter" via @property 
  print p.voltage 
  # update, similarly invoke "setter" 
  p.voltage = 12

另外,@property是在2.6、3.0新增的,2.5没有该功能。

Python 相关文章推荐
python dict 字典 以及 赋值 引用的一些实例(详解)
Jan 20 Python
对Python中type打开文件的方式介绍
Apr 28 Python
Python使用一行代码获取上个月是几月
Aug 30 Python
Python中浅拷贝copy与深拷贝deepcopy的简单理解
Oct 26 Python
python事件驱动event实现详解
Nov 21 Python
pyqt5利用pyqtDesigner实现登录界面
Mar 28 Python
pandas DataFrame的修改方法(值、列、索引)
Aug 02 Python
Python virtualenv虚拟环境实现过程解析
Apr 18 Python
python实现音乐播放和下载小程序功能
Apr 26 Python
Python函数的迭代器与生成器的示例代码
Jun 18 Python
matplotlib部件之套索Lasso的使用
Feb 24 Python
宝塔更新Python及Flask项目的部署
Apr 11 Python
Python实现类似jQuery使用中的链式调用的示例
Jun 16 #Python
浅析Python中else语句块的使用技巧
Jun 16 #Python
python基础教程之分支、循环简单用法
Jun 16 #Python
python3音乐播放器简单实现代码
Apr 20 #Python
使用python3.5仿微软记事本notepad
Jun 15 #Python
python3.5仿微软计算器程序
Mar 30 #Python
Python的Asyncore异步Socket模块及实现端口转发的例子
Jun 14 #Python
You might like
PHP中用hash实现的数组
2011/07/17 PHP
CodeIgniter框架数据库事务处理的设计缺陷和解决方案
2014/07/25 PHP
php中使用Ajax时出现Error(c00ce56e)的详细解决方案
2014/11/03 PHP
php文件管理基本功能简单操作
2017/01/16 PHP
window.addeventjs事件驱动函数集合addEvent等
2008/02/19 Javascript
jquery 多行文本框(textarea)高度变化
2013/07/03 Javascript
ff下JQuery无法监听input的keyup事件的解决方法
2013/12/12 Javascript
js用闭包遍历树状数组的方法
2014/03/19 Javascript
详解Javascript动态操作CSS
2014/12/08 Javascript
使用命令对象代替switch语句的写法示例
2015/02/28 Javascript
详解jQuery向动态生成的内容添加事件响应jQuery live()方法
2015/11/02 Javascript
深入理解React中es6创建组件this的方法
2016/08/29 Javascript
微信小程序 POST请求(网络请求)详解及实例代码
2016/11/16 Javascript
微信小程序 新建登录页并实现tabBar隐藏
2017/06/13 Javascript
Vue在页面右上角实现可悬浮/隐藏的系统菜单
2018/05/04 Javascript
浅谈Vuex注入Vue生命周期的过程
2019/05/20 Javascript
中级前端工程师必须要掌握的27个JavaScript 技巧(干货总结)
2019/09/23 Javascript
python实现的阳历转阴历(农历)算法
2014/04/25 Python
六个窍门助你提高Python运行效率
2015/06/09 Python
python3.6使用pymysql连接Mysql数据库
2018/05/25 Python
python爬虫 爬取58同城上所有城市的租房信息详解
2019/07/30 Python
Django中的FBV和CBV用法详解
2019/09/15 Python
python中删除某个元素的方法解析
2019/11/05 Python
Python实现将蓝底照片转化为白底照片功能完整实例
2019/12/13 Python
Python基于httpx模块实现发送请求
2020/07/07 Python
Python字符串及文本模式方法详解
2020/09/10 Python
应届生文秘专业个人自荐信格式
2013/09/21 职场文书
环境工程大学生个人的自我评价
2013/10/08 职场文书
应届生煤化工求职信
2013/10/21 职场文书
美术师范毕业生自荐信
2013/11/16 职场文书
护林防火标语
2014/06/27 职场文书
工厂门卫的岗位职责
2014/07/27 职场文书
党员学习中共十八大报告思想汇报
2014/09/15 职场文书
终止合同协议书范本
2016/03/22 职场文书
2016医师资格考试考生诚信考试承诺书
2016/03/25 职场文书
nginx反向代理时如何保持长连接
2021/03/31 Servers