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使用内存zipfile对象在内存中打包文件示例
Apr 30 Python
Python的Flask框架中web表单的教程
Apr 20 Python
简单谈谈Python流程控制语句
Dec 04 Python
Python存取XML的常见方法实例分析
Mar 21 Python
Python简单实现网页内容抓取功能示例
Jun 07 Python
Python使用pickle模块实现序列化功能示例
Jul 13 Python
Python应用领域和就业形势分析总结
May 14 Python
python创建子类的方法分析
Nov 28 Python
Python 限定函数参数的类型及默认值方式
Dec 24 Python
python_matplotlib改变横坐标和纵坐标上的刻度(ticks)方式
May 16 Python
keras绘制acc和loss曲线图实例
Jun 15 Python
Pytorch之Tensor和Numpy之间的转换的实现方法
Sep 03 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
第五节--克隆
2006/11/16 PHP
自动生成文章摘要的代码[PHP 版本]
2007/03/20 PHP
深入解析php中的foreach函数
2013/08/31 PHP
PHP实现的AES 128位加密算法示例
2019/09/16 PHP
javascript 面向对象编程基础 多态
2009/08/21 Javascript
jQuery 下拉列表 二级联动插件分享
2012/03/29 Javascript
使用javascript实现ListBox左右全选,单选,多选,全请
2013/11/07 Javascript
js实现div在页面拖动效果
2016/05/04 Javascript
Jquery针对tr td的一些实用操作方法(必看篇)
2016/10/05 Javascript
详解React开发中使用require.ensure()按需加载ES6组件
2017/05/12 Javascript
Bootstrap3.3.7导航栏下拉菜单鼠标滑过展开效果
2017/10/31 Javascript
two.js之实现动画效果示例
2017/11/06 Javascript
详解微信小程序之一键复制到剪切板
2019/04/24 Javascript
Node.js 的 GC 机制详解
2019/06/03 Javascript
微信小程序-API接口安全详解
2019/07/16 Javascript
用vite搭建vue3应用的实现方法
2021/02/22 Vue.js
python将ip地址转换成整数的方法
2015/03/17 Python
详解python发送各类邮件的主要方法
2016/12/22 Python
正确理解python中的关键字“with”与上下文管理器
2017/04/21 Python
启动targetcli时遇到错误解决办法
2017/10/26 Python
python使用tornado实现登录和登出
2018/07/28 Python
教你利用Python玩转histogram直方图的五种方法
2018/07/30 Python
Python测试模块doctest使用解析
2019/08/10 Python
python实现根据给定坐标点生成多边形mask的例子
2020/02/18 Python
Python之字典对象的几种创建方法
2020/09/30 Python
用OpenCV进行年龄和性别检测的实现示例
2021/01/29 Python
仿酷狗html5手机音乐播放器主要部分代码
2013/05/15 HTML / CSS
四年大学生活的个人自我评价
2013/12/11 职场文书
政府信息公开实施方案
2014/05/09 职场文书
关于奉献的演讲稿
2014/05/21 职场文书
幸福家庭事迹材料
2014/12/20 职场文书
教师考核评语大全
2014/12/31 职场文书
党员反四风学习心得体会
2016/01/22 职场文书
2019年励志签名:致拼搏路上的自己
2019/10/11 职场文书
Redis命令处理过程源码解析
2022/02/12 Redis
详解在OpenCV中如何使用图像像素
2022/03/03 Python