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中os操作文件及文件路径实例汇总
Jan 15 Python
LRUCache的实现原理及利用python实现的方法
Nov 21 Python
Python实现的当前时间多加一天、一小时、一分钟操作示例
May 21 Python
详解Python 数据库的Connection、Cursor两大对象
Jun 25 Python
python 实现求解字符串集的最长公共前缀方法
Jul 20 Python
Python使用sklearn库实现的各种分类算法简单应用小结
Jul 04 Python
python实现简单聊天室功能 可以私聊
Jul 12 Python
ipad上运行python的方法步骤
Oct 12 Python
python list等分并从等分的子集中随机选取一个数
Nov 16 Python
Python爬虫如何破解JS加密的Cookie
Nov 19 Python
Python对excel的基本操作方法
Feb 18 Python
浅谈Python基础之列表那些事儿
May 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
人工智能开始玩《星际争霸2》 你的操作跟得上吗?
2017/08/11 星际争霸
发布一个用PHP fsockopen写的HTTP下载的类
2007/02/22 PHP
php实现根据url自动生成缩略图的方法
2014/09/23 PHP
thinkphp3.2.3版本的数据库增删改查实现代码
2016/09/22 PHP
php解决约瑟夫环算法实例分析
2019/09/30 PHP
常用简易JavaScript函数
2009/04/09 Javascript
jQuery EasyUI NumberBox(数字框)的用法
2010/07/08 Javascript
各浏览器对click方法的支持差异小结
2011/07/31 Javascript
javascript自动改变文字大小和颜色的效果的小例子
2013/08/02 Javascript
window.onresize 多次触发的解决方法
2013/11/08 Javascript
jsPDF导出pdf示例
2014/05/02 Javascript
如何防止回车(enter)键提交表单
2014/05/11 Javascript
详解Wondows下Node.js使用MongoDB的环境配置
2016/03/01 Javascript
bootstrap按钮插件(Button)使用方法解析
2017/01/13 Javascript
React-Native使用Mobx实现购物车功能
2017/09/14 Javascript
Vue中用props给data赋初始值遇到的问题解决
2018/11/27 Javascript
JS函数进阶之继承用法实例分析
2020/01/15 Javascript
微信小程序如何实现radio单选框单击打勾和取消
2020/01/21 Javascript
基于elementUI竖向表格、和并列的案例
2020/10/26 Javascript
[48:02]Ti4循环赛第三日 VG vs Liquid和NEWBEE vs DK
2014/07/12 DOTA
跟老齐学Python之网站的结构
2014/10/24 Python
Python实现从log日志中提取ip的方法【正则提取】
2018/03/31 Python
Python实现随机创建电话号码的方法示例
2018/12/07 Python
Python 实现输入任意多个数,并计算其平均值的例子
2019/07/16 Python
Python实现寻找回文数字过程解析
2020/06/09 Python
Pycharm新手使用教程(图文详解)
2020/09/17 Python
香港化妆品经销商:我的公主
2016/08/05 全球购物
维多利亚的秘密官方网站:Victoria’s Secret
2018/10/24 全球购物
澳大利亚最早和最古老的巨型游戏专家:Yardgames
2020/02/20 全球购物
任命书怎么写
2014/06/04 职场文书
内科护士节演讲稿
2014/09/11 职场文书
房地产销售主管岗位职责
2015/02/13 职场文书
郭明义观后感
2015/06/08 职场文书
开业庆典致辞
2015/08/01 职场文书
2016大学迎新欢迎词
2015/09/29 职场文书
pytorch显存一直变大的解决方案
2021/04/08 Python