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爬虫入门教程之点点美女图片爬虫代码分享
Sep 02 Python
pygame 精灵的行走及二段跳的实现方法(必看篇)
Jul 10 Python
python爬虫之BeautifulSoup 使用select方法详解
Oct 23 Python
python 平衡二叉树实现代码示例
Jul 07 Python
详解python里的命名规范
Jul 16 Python
解决Python3中的中文字符编码的问题
Jul 18 Python
win10 64bit下python NLTK安装教程
Sep 19 Python
基于django channel实现websocket的聊天室的方法示例
Apr 11 Python
PyCharm+Qt Designer+PyUIC安装配置教程详解
Jun 13 Python
Django的models中on_delete参数详解
Jul 16 Python
基于Pytorch版yolov5的滑块验证码破解思路详解
Feb 25 Python
Python 数据可视化之Seaborn详解
Nov 02 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正则匹配反斜杠'\'和美元'$'的方法
2017/02/08 PHP
阿里对象存储OSS在laravel框架中的使用方法
2019/10/13 PHP
jQuery实现公告文字左右滚动的实例代码
2013/10/29 Javascript
使用Plupload实现直接上传附件至七牛云存储
2014/12/26 Javascript
js实现select下拉框菜单
2015/12/08 Javascript
实现easyui的datagrid导出为excel的示例代码
2016/11/10 Javascript
JavaScript仿百度图片浏览效果
2016/11/23 Javascript
node.js 发布订阅模式的实例
2017/09/10 Javascript
IntelliJ IDEA 安装vue开发插件的方法
2017/11/21 Javascript
jQuery实现动态控制页面元素的方法分析
2017/12/20 jQuery
Vue利用canvas实现移动端手写板的方法
2018/05/03 Javascript
Vue.js实现的购物车功能详解
2019/01/27 Javascript
nodejs log4js 使用详解
2019/05/31 NodeJs
Vue编程式跳转的实例代码详解
2019/07/10 Javascript
vue中使用element组件时事件想要传递其他参数的问题
2019/09/18 Javascript
微信小程序自定义模态弹窗组件详解
2019/12/24 Javascript
python修改字典内key对应值的方法
2015/07/11 Python
python中函数默认值使用注意点详解
2016/06/01 Python
python版简单工厂模式
2017/10/16 Python
Python中的探索性数据分析(功能式)
2017/12/22 Python
Python中dict和set的用法讲解
2019/03/28 Python
Python网络爬虫之爬取微博热搜
2019/04/18 Python
python实现单链表的方法示例
2019/09/03 Python
Python生成随机验证码代码实例解析
2020/06/09 Python
使用Keras建立模型并训练等一系列操作方式
2020/07/02 Python
python删除文件、清空目录的实现方法
2020/09/23 Python
Python实现JS解密并爬取某音漫客网站
2020/10/23 Python
html5组织文档结构_动力节点Java学院整理
2017/07/11 HTML / CSS
主持人演讲稿
2014/05/13 职场文书
新闻传播专业求职信
2014/07/22 职场文书
教师批评与自我批评材料
2014/10/16 职场文书
群众路线个人整改措施
2014/10/24 职场文书
委托公证书样本
2015/01/23 职场文书
校园运动会广播稿
2015/08/19 职场文书
django如何自定义manage.py管理命令
2021/04/27 Python
matplotlib如何设置坐标轴刻度的个数及标签的方法总结
2021/06/11 Python