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中urllib2模块的8个使用细节分享
Jan 01 Python
python开发中range()函数用法实例分析
Nov 12 Python
Python线性方程组求解运算示例
Jan 17 Python
Python实现通讯录功能
Feb 22 Python
使用Eclipse如何开发python脚本
Apr 11 Python
python2.7实现FTP文件下载功能
Apr 15 Python
python pycurl验证basic和digest认证的方法
May 02 Python
python 对key为时间的dict排序方法
Oct 17 Python
python绘制散点图并标记序号的方法
Dec 11 Python
python中将两组数据放在一起按照某一固定顺序shuffle的实例
Jul 15 Python
python连接PostgreSQL过程解析
Feb 09 Python
使用pycharm和pylint检查python代码规范操作
Jun 09 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将数据库中所有内容生成静态html文档的代码
2010/04/12 PHP
php用数组返回无限分类的列表数据的代码
2010/08/08 PHP
完美实现GIF动画缩略图的php代码
2011/01/02 PHP
php的POSIX 函数以及进程测试的深入分析
2013/06/03 PHP
PHP依赖倒置(Dependency Injection)代码实例
2014/10/11 PHP
PHP new static 和 new self详解
2017/02/19 PHP
thinkphp5框架扩展redis类方法示例
2019/05/06 PHP
PHP7 错误处理机制修改
2021/03/09 PHP
jQuery 1.3 和 Validation 验证插件1.5.1
2009/07/09 Javascript
jQuery库与其他JS库冲突的解决办法
2010/02/07 Javascript
各浏览器中querySelector和querySelectorAll的实现差异分析
2012/05/23 Javascript
javascript 实现 原路返回
2015/01/21 Javascript
JavaScript实现彩虹文字效果的方法
2015/04/16 Javascript
jquery实现Li滚动时滚动条自动添加样式的方法
2015/08/10 Javascript
基于百度地图实现产品销售的单位位置查看功能设计与实现
2016/10/21 Javascript
JS中微信小程序自定义底部弹出框
2016/12/22 Javascript
VUE中的无限循环代码解析
2017/09/22 Javascript
为nuxt项目写一个面包屑cli工具实现自动生成页面与面包屑配置
2019/09/29 Javascript
JS实现图片切换特效
2019/12/23 Javascript
vue开发chrome插件,实现获取界面数据和保存到数据库功能
2020/12/01 Vue.js
python开发之字符串string操作方法实例详解
2015/11/12 Python
python实现简单飞行棋
2020/02/06 Python
python词云库wordcloud的使用方法与实例详解
2020/02/17 Python
python 实现学生信息管理系统的示例
2020/11/28 Python
Python之京东商品秒杀的实现示例
2021/01/06 Python
Python实现Excel自动分组合并单元格
2021/02/22 Python
用Python匹配HTML tag的时候,<.*>和<.*?>有什么区别
2012/11/04 面试题
大学生求职简历的自我评价
2013/10/14 职场文书
主治医师岗位职责
2013/12/10 职场文书
运动会广播稿500字
2014/01/28 职场文书
高三学习决心书
2014/03/11 职场文书
学习雷锋月活动总结
2014/07/03 职场文书
公司前台接待岗位职责
2015/04/03 职场文书
2015年药店店长工作总结
2015/04/29 职场文书
MySQL里面的子查询的基本使用
2021/08/02 MySQL
如何用六步教会你使用python爬虫爬取数据
2022/04/06 Python