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基于pandas实现json格式转换成dataframe的方法
Jun 22 Python
基于Django框架利用Ajax实现点赞功能实例代码
Aug 19 Python
由Python编写的MySQL管理工具代码实例
Apr 09 Python
pyqt5 使用label控件实时显示时间的实例
Jun 14 Python
Python 占位符的使用方法详解
Jul 10 Python
PyTorch实现ResNet50、ResNet101和ResNet152示例
Jan 14 Python
使用Tkinter制作信息提示框
Feb 18 Python
python 操作mysql数据中fetchone()和fetchall()方式
May 15 Python
Python叠加矩形框图层2种方法及效果
Jun 18 Python
python定义类的简单用法
Jul 24 Python
python开发飞机大战游戏
Jul 15 Python
pd.drop_duplicates删除重复行的方法实现
Jun 16 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
新版mysql+apache+php Linux安装指南
2006/10/09 PHP
main.php
2006/12/09 PHP
php轻松实现中英文混排字符串截取
2014/05/28 PHP
php实现建立多层级目录的方法
2014/07/19 PHP
PHP魔术方法以及关于独立实例与相连实例的全面讲解
2016/10/18 PHP
Yii框架用户登录session丢失问题解决方法
2017/01/07 PHP
jQuery 开发者应该注意的9个错误
2012/05/03 Javascript
js弹出层(jQuery插件形式附带reLoad功能)
2013/04/12 Javascript
使用mouse事件实现简单的鼠标经过特效
2015/01/30 Javascript
js中flexible.js实现淘宝弹性布局方案
2020/06/23 Javascript
angularjs表格分页功能详解
2016/01/21 Javascript
JavaScript+html5 canvas制作色彩斑斓的正方形效果
2016/01/27 Javascript
javascript实现方法调用与方法触发小结
2016/03/26 Javascript
浅谈js中的变量名和函数名重名
2017/02/13 Javascript
Bootstrap Table使用整理(三)
2017/06/09 Javascript
浅谈Vue2.0中v-for迭代语法的变化(key、index)
2018/03/06 Javascript
通过vue手动封装on、emit、off的代码详解
2019/05/29 Javascript
JS实现数组删除指定元素功能示例
2019/06/05 Javascript
微信头像地址失效踩坑记附带解决方案
2019/09/23 Javascript
[44:43]完美世界DOTA2联赛决赛日 FTD vs GXR 第一场 11.08
2020/11/11 DOTA
Python translator使用实例
2008/09/06 Python
Windows中安装使用Virtualenv来创建独立Python环境
2016/05/31 Python
在python plt图表中文字大小调节的方法
2019/07/08 Python
解决TensorFlow GPU版出现OOM错误的问题
2020/02/03 Python
windows python3安装Jupyter Notebooks教程
2020/04/13 Python
django实现日志按日期分割
2020/05/21 Python
CSS3 边框效果
2019/11/04 HTML / CSS
意大利在线购买隐形眼镜网站:VisionDirect.it
2019/03/18 全球购物
公司营业员的工作总结自我评价
2013/10/05 职场文书
护理专业毕业生自我鉴定
2013/10/08 职场文书
四下基层实施方案
2014/03/28 职场文书
银行求职信范文
2014/05/26 职场文书
单身证明格式样本
2015/06/15 职场文书
道士塔读书笔记
2015/06/30 职场文书
redis不能访问本机真实ip地址的解决方案
2021/07/07 Redis
《遗弃》开发商删推文要跑路?官方回应:还在开发
2022/04/03 其他游戏