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实现读取目录所有文件的文件名并保存到txt文件代码
Nov 22 Python
Python中二维列表如何获取子区域元素的组成
Jan 19 Python
Python模拟鼠标点击实现方法(将通过实例自动化模拟在360浏览器中自动搜索python)
Aug 23 Python
Python制作词云的方法
Jan 03 Python
Python使用numpy实现BP神经网络
Mar 10 Python
Python操作配置文件ini的三种方法讲解
Feb 22 Python
详解python之heapq模块及排序操作
Apr 04 Python
Python迭代器iterator生成器generator使用解析
Oct 24 Python
TensorFlow实现从txt文件读取数据
Feb 05 Python
Django Haystack 全文检索与关键词高亮的实现
Feb 17 Python
python GUI库图形界面开发之PyQt5表单布局控件QFormLayout详细使用方法与实例
Mar 06 Python
如何用python爬取微博热搜数据并保存
Feb 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
phpMyAdmin 安装教程全攻略
2007/03/19 PHP
php入门学习知识点七 PHP函数的基本应用
2011/07/14 PHP
ThinkPHP项目分组配置方法分析
2016/03/23 PHP
Zend Framework实现自定义过滤器的方法
2016/12/09 PHP
jquery maxlength使用说明
2011/09/09 Javascript
JavaScript高级程序设计(第3版)学习笔记9 js函数(下)
2012/10/11 Javascript
js中 javascript:void(0) 用法详解
2015/08/11 Javascript
分离与继承的思想实现图片上传后的预览功能:ImageUploadView
2016/04/07 Javascript
js制作支付倒计时页面
2016/10/21 Javascript
JavaScript设计模式之策略模式详解
2017/06/09 Javascript
Spring Boot/VUE中路由传递参数的实现代码
2018/03/02 Javascript
打通前后端构建一个Vue+Express的开发环境
2018/07/17 Javascript
JavaScript实现表单注册、表单验证、运算符功能
2018/10/15 Javascript
9102了,你还不会移动端真机调试吗
2019/03/25 Javascript
js函数柯里化的方法和作用实例分析
2020/04/11 Javascript
Vue列表如何实现滚动到指定位置样式改变效果
2020/05/09 Javascript
[43:51]2018DOTA2亚洲邀请赛3月30日 小组赛B组 EG VS Secret
2018/03/31 DOTA
[34:41]夜魇凡尔赛茶话会 第二期02:你画我猜
2021/03/11 DOTA
Python MySQL数据库连接池组件pymysqlpool详解
2017/07/07 Python
浅谈python日志的配置文件路径问题
2018/04/28 Python
Python实现将多个空格换为一个空格.md的方法
2018/12/20 Python
深入学习python多线程与GIL
2019/08/26 Python
python 图像处理画一个正弦函数代码实例
2019/09/10 Python
python tkinter之 复选、文本、下拉的实现
2020/03/04 Python
Python爬虫HTPP请求方法有哪些
2020/06/03 Python
浅析Python 中的 WSGI 接口和 WSGI 服务的运行
2020/12/09 Python
Omio意大利:全欧洲低价大巴、火车和航班搜索和比价
2017/12/02 全球购物
美国批发供应商:Kole Imports
2019/04/10 全球购物
以下的初始化有什么区别
2013/12/16 面试题
自荐信模版
2013/10/24 职场文书
怎样写好自荐信和推荐信
2013/12/26 职场文书
致400米运动员广播稿
2014/02/07 职场文书
优秀员工演讲稿
2014/05/19 职场文书
大学生应聘导游自荐信
2014/06/02 职场文书
监察局领导班子四风问题整改措施思想汇报
2014/10/05 职场文书
2015年推普周活动方案
2015/05/06 职场文书