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 相关文章推荐
使用setup.py安装python包和卸载python包的方法
Nov 27 Python
python实现保存网页到本地示例
Mar 16 Python
python打开网页和暂停实例
Sep 30 Python
python操作ssh实现服务器日志下载的方法
Jun 03 Python
Python 安装setuptools和pip工具操作方法(必看)
May 22 Python
Python OpenCV处理图像之图像直方图和反向投影
Jul 10 Python
Django上线部署之IIS的配置方法
Aug 22 Python
简单了解python协程的相关知识
Aug 31 Python
开启Django博客的RSS功能的实现方法
Feb 17 Python
如何使用python传入不确定个数参数
Feb 18 Python
基于python实现获取网页图片过程解析
May 11 Python
Python快速优雅的批量修改Word文档样式
May 20 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
PHP4实际应用经验篇(9)
2006/10/09 PHP
php date与gmdate的获取日期的区别
2010/02/08 PHP
PHP处理SQL脚本文件导入到MySQL的代码实例
2014/03/17 PHP
Drupal简体中文语言包安装教程
2014/09/27 PHP
Yii2中添加全局函数的方法分析
2017/05/04 PHP
分享一个asp.net pager分页控件
2012/01/04 Javascript
js中scrollHeight,scrollWidth,scrollLeft,scrolltop等差别介绍
2012/05/16 Javascript
JavaScript的21条基本知识点
2014/03/04 Javascript
jquery实现的一个文章自定义分段显示功能
2014/05/23 Javascript
jQuery固定浮动侧边栏实现思路及代码
2014/09/28 Javascript
javascript数组详解
2014/10/22 Javascript
js事件冒泡、事件捕获和阻止默认事件详解
2016/08/04 Javascript
浅谈MVC+EF easyui dataGrid 动态加载分页表格
2016/11/10 Javascript
Bootstrap导航条可点击和鼠标悬停显示下拉菜单
2016/11/25 Javascript
jQuery实现最简单实用的分秒倒计时
2017/02/05 Javascript
解决URL地址中的中文乱码问题的办法
2017/02/10 Javascript
微信小程序自定义组件
2017/08/16 Javascript
React Router v4 入坑指南(小结)
2018/04/08 Javascript
JS简单生成由字母数字组合随机字符串示例
2018/05/25 Javascript
Angular 利用路由跳转到指定页面的指定位置方法
2018/08/31 Javascript
微信小程序button标签open-type属性原理解析
2020/01/21 Javascript
JavaScript装箱及拆箱boxing及unBoxing用法解析
2020/06/15 Javascript
详解js中的原型,原型对象,原型链
2020/07/16 Javascript
matplotlib 输出保存指定尺寸的图片方法
2018/05/24 Python
PyCharm 设置SciView工具窗口的方法
2019/01/15 Python
Python3显示当前时间、计算时间差及时间加减法示例代码
2019/09/07 Python
使用Pyhton 分析酒店针孔摄像头
2020/03/04 Python
美国钻石商店:Zales
2016/11/20 全球购物
荷兰包包购物网站:The Little Green Bag
2018/03/17 全球购物
美国购买体育、音乐会和剧院门票网站:SelectATicket
2019/09/08 全球购物
责任书格式范文
2014/07/28 职场文书
机关党总支领导班子整改方案
2014/09/20 职场文书
简历自荐信范文
2015/03/09 职场文书
社区党支部公开承诺书
2015/04/29 职场文书
离职证明格式样本
2015/06/12 职场文书
2016年秋季运动会加油稿
2015/12/21 职场文书