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实现的文件同步服务器实例
Jun 02 Python
Python第三方库的安装方法总结
Jun 06 Python
详解django三种文件下载方式
Apr 06 Python
python筛选出两个文件中重复行的方法
May 31 Python
Win8下python3.5.1安装教程
Jul 29 Python
Python实现 版本号对比功能的实例代码
Apr 18 Python
Python3实现定时任务的四种方式
Jun 03 Python
python 搜索大文件的实例代码
Jul 08 Python
详解将Python程序(.py)转换为Windows可执行文件(.exe)
Jul 19 Python
django orm模块中的 is_delete用法
May 20 Python
Matplotlib中rcParams使用方法
Jan 05 Python
pycharm代码删除恢复的方法
Jun 26 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抓取远程图片(含不带后缀的)教程详解
2016/10/21 PHP
javascript中关于执行环境的杂谈
2011/08/14 Javascript
js中回调函数的学习笔记
2014/07/31 Javascript
jQuery中bind()方法用法实例
2015/01/19 Javascript
Javascript点击按钮随机改变数字与其颜色
2016/09/01 Javascript
前端主流框架vue学习笔记第一篇
2017/07/26 Javascript
jQuery 利用ztree实现树形表格的实例代码
2017/09/27 jQuery
详解用场景去理解函数柯里化(入门篇)
2019/04/11 Javascript
一个手写的vue放大镜效果
2019/08/09 Javascript
详解javascript void(0)
2020/07/13 Javascript
浅谈JavaScript中的“!!”作用
2020/08/03 Javascript
浅谈在vue-cli3项目中解决动态引入图片img404的问题
2020/08/04 Javascript
wxPython框架类和面板类的使用实例
2014/09/28 Python
python模仿网页版微信发送消息功能
2018/02/24 Python
TensorFlow深度学习之卷积神经网络CNN
2018/03/09 Python
Pandas实现数据类型转换的一些小技巧汇总
2018/05/07 Python
pandas.DataFrame.to_json按行转json的方法
2018/06/05 Python
Python 200行代码实现一个滑动验证码过程详解
2019/07/11 Python
python实现两张图片拼接为一张图片并保存
2019/07/16 Python
Django中使用session保持用户登陆连接的例子
2019/08/06 Python
Python实现时间序列可视化的方法
2019/08/06 Python
tensorflow实现打印ckpt模型保存下的变量名称及变量值
2020/01/04 Python
python3通过qq邮箱发送邮件以及附件
2020/05/20 Python
Python轻量级web框架bottle使用方法解析
2020/06/13 Python
详解Python openpyxl库的基本应用
2021/02/26 Python
DVF官方网站:美国时装界尊尚品牌
2017/08/29 全球购物
照片礼物和装饰:MyPhoto
2019/11/02 全球购物
ECHT官方网站:男女健身服
2020/02/14 全球购物
乌克兰移动电子产品和相关配件的在线商店:iTMag
2020/03/16 全球购物
娇韵诗俄罗斯官方网站:Clarins俄罗斯
2020/10/03 全球购物
一些PHP的面试题
2015/05/06 面试题
2015年试用期工作总结
2014/12/12 职场文书
拾金不昧感谢信
2015/01/21 职场文书
小学教师读书笔记
2015/07/01 职场文书
2015年暑期见闻
2015/07/14 职场文书
2019年个人工作总结范文
2019/03/25 职场文书