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程序中自定义异常的方法
Oct 16 Python
Python实现类似jQuery使用中的链式调用的示例
Jun 16 Python
python基础练习之几个简单的游戏
Nov 10 Python
对python中的for循环和range内置函数详解
Apr 17 Python
python使用matplotlib库生成随机漫步图
Aug 27 Python
Python字符串匹配之6种方法的使用详解
Apr 08 Python
Python实现获取系统临时目录及临时文件的方法示例
Jun 26 Python
解决Atom安装Hydrogen无法运行python3的问题
Aug 28 Python
python实现静态web服务器
Sep 03 Python
python selenium实现发送带附件的邮件代码实例
Dec 10 Python
python使用rsa非对称加密过程解析
Dec 28 Python
python实现3D地图可视化
Mar 25 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允许外网访问
2013/06/04 PHP
php 验证码(倾斜,正弦干扰线,黏贴,旋转)
2013/06/29 PHP
简单说说PHP优化那些事(经验分享)
2014/11/27 PHP
解决form中action属性后面?传递参数 获取不到的问题
2017/07/21 PHP
PHP+redis实现微博的推模型案例分析
2019/07/10 PHP
PHP 实现缩略图
2021/03/09 PHP
javascript setAttribute, getAttribute 在不同浏览器上的不同表现
2010/08/05 Javascript
一样的table?不一样的table(可编辑状态table)
2012/09/19 Javascript
JQuery验证jsp页面属性是否为空(实例代码)
2013/11/08 Javascript
javascript实时显示当天日期的方法
2015/05/20 Javascript
js实现防止被iframe的方法
2015/07/03 Javascript
Jquery操作Ajax方法小结
2015/11/29 Javascript
JavaScript实现图片自动加载的瀑布流效果
2016/04/11 Javascript
Nodejs中 npm常用命令详解
2016/07/04 NodeJs
AngularJS使用ng-Cloak阻止初始化闪烁问题的方法
2016/11/03 Javascript
Vue中的v-cloak使用解读
2017/03/27 Javascript
node.js自动上传ftp的脚本分享
2018/06/16 Javascript
jQuery实现左右两个列表框的内容相互移动功能示例
2019/01/27 jQuery
Vue组件之高德地图地址选择功能的实例代码
2019/06/21 Javascript
前端vue如何使用高德地图
2020/11/05 Javascript
在Python中使用CasperJS获取JS渲染生成的HTML内容的教程
2015/04/09 Python
Python出现segfault错误解决方法
2016/04/16 Python
对Python捕获控制台输出流的方法详解
2019/01/07 Python
Python中类似于jquery的pyquery库用法分析
2019/12/02 Python
Pytorch损失函数nn.NLLLoss2d()用法说明
2020/07/07 Python
CSS实现雨滴动画效果的实例代码
2019/10/08 HTML / CSS
html5图片上传预览示例分享
2014/04/14 HTML / CSS
俄罗斯在线服装店:STOLNIK
2021/03/07 全球购物
运动鞋、街头服装、手表和手袋的实时市场:StockX
2020/11/25 全球购物
Prototype如何为一个Ajax添加一个参数
2015/12/06 面试题
幼儿园保教管理制度
2014/02/03 职场文书
教师评语大全
2014/04/28 职场文书
中学生检讨书1000字
2014/10/28 职场文书
2014年置业顾问工作总结
2014/11/17 职场文书
2016年教师党员公开承诺书
2016/03/24 职场文书
Python pandas读取CSV文件的注意事项(适合新手)
2021/06/20 Python