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 相关文章推荐
可用于监控 mysql Master Slave 状态的python代码
Feb 10 Python
跟老齐学Python之关于类的初步认识
Oct 11 Python
Django URL传递参数的方法总结
Aug 28 Python
浅谈使用Python内置函数getattr实现分发模式
Jan 22 Python
python3+PyQt5实现文档打印功能
Apr 24 Python
pygame游戏之旅 添加碰撞效果的方法
Nov 20 Python
python+openCV利用摄像头实现人员活动检测
Jun 22 Python
numpy.meshgrid()理解(小结)
Aug 01 Python
使用虚拟环境打包python为exe 文件的方法
Aug 29 Python
python获取网络图片方法及整理过程详解
Dec 20 Python
python3获取文件中url内容并下载代码实例
Dec 27 Python
Tensorflow中的dropout的使用方法
Mar 13 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的ob_start来生成静态页面的方法分析
2011/03/09 PHP
php带抄送和密件抄送的邮件发送方法
2015/03/20 PHP
微信小程序发送订阅消息的方法(php 为例)
2019/10/30 PHP
jQuery 常见学习网站与参考书
2009/11/09 Javascript
jQuery 表单验证扩展代码(一)
2010/10/11 Javascript
web开发人员学习jQuery的6大理由及jQuery的优势介绍
2013/01/03 Javascript
解决JS浮点数运算出现Bug的方法
2013/03/12 Javascript
JavaScript 获取任一float型小数点后两位的小数
2014/06/30 Javascript
浅谈jquery之on()绑定事件和off()解除绑定事件
2016/10/26 Javascript
学习vue.js条件渲染
2016/12/03 Javascript
详解Vue.js入门环境搭建
2017/03/17 Javascript
JavaScript的继承实现小结
2017/05/07 Javascript
React全家桶环境搭建过程详解
2018/05/18 Javascript
Vue框架下引入ActiveX控件的问题解决
2019/03/25 Javascript
vue 中 beforeRouteEnter 死循环的问题
2019/04/23 Javascript
jQuery实现动态加载瀑布流
2020/09/01 jQuery
详解vue实现坐标拾取器功能示例
2020/11/18 Vue.js
[02:24]DOTA2痛苦女王 英雄基础教程
2013/11/26 DOTA
[04:52]2015国际邀请赛LGD战队晋级之路
2015/08/14 DOTA
Python的语言类型(详解)
2017/06/24 Python
python Matplotlib画图之调整字体大小的示例
2017/11/20 Python
解决csv.writer写入文件有多余的空行问题
2018/07/06 Python
python实现事件驱动
2018/11/21 Python
浅析PyTorch中nn.Linear的使用
2019/08/18 Python
python实现加密的方式总结
2020/01/19 Python
Keras 快速解决OOM超内存的问题
2020/06/11 Python
基于Python的一个自动录入表格的小程序
2020/08/05 Python
python如何使用腾讯云发送短信
2020/09/17 Python
selenium框架中driver.close()和driver.quit()关闭浏览器
2020/12/08 Python
欧洲领先的电子和电信零售商和服务提供商:Currys PC World Business
2017/12/05 全球购物
size?丹麦官网:英国伦敦的球鞋精品店
2019/04/15 全球购物
企业项目策划书
2014/01/11 职场文书
电子专业求职信
2014/06/19 职场文书
离婚协议书怎样才有法律效力
2014/10/10 职场文书
汽车转让协议书范本
2014/12/07 职场文书
会计专业求职信范文
2015/03/19 职场文书