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 struct.unpack
Sep 06 Python
简介Python中用于处理字符串的center()方法
May 18 Python
Python如何快速实现分布式任务
Jul 06 Python
使用python将请求的requests headers参数格式化方法
Jan 02 Python
pycharm 关掉syntax检查操作
Jun 09 Python
keras.utils.to_categorical和one hot格式解析
Jul 02 Python
Python threading模块condition原理及运行流程详解
Oct 05 Python
Python headers请求头如何实现快速添加
Nov 03 Python
Python绘制数码晶体管日期
Feb 19 Python
Pytorch 中的optimizer使用说明
Mar 03 Python
Python机器学习之PCA降维算法详解
May 19 Python
Python Matplotlib绘制条形图的全过程
Oct 24 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入门学习知识点六 PHP文件的读写操作代码
2011/07/14 PHP
两个SUBMIT按钮,如何区分处理
2006/08/22 Javascript
jQuery EasyUI API 中文文档 - ValidateBox验证框
2011/10/06 Javascript
JavaScript之Getters和Setters 平台支持等详细介绍
2012/12/07 Javascript
js中split和replace的用法实例
2015/02/28 Javascript
javascript实现跨域的方法汇总
2015/06/25 Javascript
jQuery实现鼠标经过弹出提示信息的地图热点效果
2015/08/07 Javascript
jQuery控制li上下循环滚动插件用法实例(附demo源码下载)
2016/05/28 Javascript
bootstrap输入框组代码分享
2016/06/07 Javascript
Angular指令封装jQuery日期时间插件datetimepicker实现双向绑定示例
2017/01/22 Javascript
动态统计当前输入内容的字节、字符数的实例详解
2017/10/27 Javascript
解决easyui日期时间框ie的兼容的问题
2018/03/01 Javascript
解决layui中table异步数据请求不支持自定义返回数据格式的问题
2018/08/19 Javascript
微信小程序文章详情页面实现代码
2018/09/10 Javascript
js实现网页同时进行多个倒计时功能
2019/02/25 Javascript
[jQuery] 事件和动画详解
2019/03/05 jQuery
electron-vue利用webpack打包实现多页面的入口文件问题
2019/05/12 Javascript
JavaScript实现动态留言板
2020/03/16 Javascript
在vue中实现禁止回退上一步,路由不存历史记录
2020/07/22 Javascript
使用Python的Supervisor进行进程监控以及自动启动
2014/05/29 Python
python使用matplotlib绘图时图例显示问题的解决
2017/04/27 Python
Python cookbook(数据结构与算法)同时对数据做转换和换算处理操作示例
2018/03/23 Python
Python numpy中矩阵的基本用法汇总
2019/02/12 Python
python批量替换文件名中的共同字符实例
2020/03/05 Python
有趣的Python图片制作之如何用QQ好友头像拼接出里昂
2020/04/22 Python
利用canvas实现图片下载功能来实现浏览器兼容问题
2019/05/31 HTML / CSS
canvas使用注意点总结
2013/07/19 HTML / CSS
泰国Robinson百货官网:购买知名品牌的商品
2020/02/08 全球购物
营销主管自我评价怎么写
2013/09/19 职场文书
优秀毕业生自荐信范文
2014/01/01 职场文书
农林环境专业求职信
2014/03/13 职场文书
大学生见习期满自我鉴定
2014/09/13 职场文书
高考升学宴答谢词
2015/01/20 职场文书
北京颐和园导游词
2015/01/30 职场文书
保险内勤岗位职责
2015/04/13 职场文书
开除通知书范本
2015/04/25 职场文书