Python中的descriptor描述器简明使用指南


Posted in Python onJune 02, 2016

当定义迭代器的时候,描述是实现迭代协议的对象,即实现__iter__方法的对象。同理,所谓描述器,即实现了描述符协议,即__get__, __set__, 和 __delete__方法的对象。

单看定义,还是比较抽象的。talk is cheap。看代码吧:

class WebFramework(object):
  def __init__(self, name='Flask'):
    self.name = name

  def __get__(self, instance, owner):
    return self.name

  def __set__(self, instance, value):
    self.name = value


class PythonSite(object):

  webframework = WebFramework()

In [1]: PythonSite.webframework
Out[1]: 'Flask'

In [2]: PythonSite.webframework = 'Tornado'

In [3]: PythonSite.webframework
Out[3]: 'Tornado'

定义了一个类WebFramework,它实现了描述符协议__get__和__set__,该对象(类也是对象,一切都是对象)即成为了一个描述器。同时实现__get__和__set__的称之为资料描述器(data descriptor)。仅仅实现__get__的则为非描述器。两者的差别是相对于实例的字典的优先级。

如果实例字典中有与描述器同名的属性,如果描述器是资料描述器,优先使用资料描述器,如果是非资料描述器,优先使用字典中的属性。

描述器的调用
对于这类魔法,其调用方法往往不是直接使用的。例如装饰器需要用 @ 符号调用。迭代器通常在迭代过程,或者使用 next 方法调用。描述器则比较简单,对象属性的时候会调用。

In [15]: webframework = WebFramework()

In [16]: webframework.__get__(webframework, WebFramework)
Out[16]: 'Flask'

描述器的应用
描述器的作用主要在方法和属性的定义上。既然我们可以重新描述类的属性,那么这个魔法就可以改变类的一些行为。最简单的应用则是可以配合装饰器,写一个类属性的缓存。Flask的作者写了一个werkzeug网络工具库,里面就使用描述器的特性,实现了一个缓存器。

class _Missing(object):
  def __repr__(self):
    return 'no value'

  def __reduce__(self):
    return '_missing'


_missing = _Missing()


class cached_property(object):
  def __init__(self, func, name=None, doc=None):
    self.__name__ = name or func.__name__
    self.__module__ = func.__module__
    self.__doc__ = doc or func.__doc__
    self.func = func

  def __get__(self, obj, type=None):
    if obj is None:
      return self
    value = obj.__dict__.get(self.__name__, _missing)
    if value is _missing:
      value = self.func(obj)
      obj.__dict__[self.__name__] = value
    return value


class Foo(object):
  @cached_property
  def foo(self):
    print 'first calculate'
    result = 'this is result'
    return result


f = Foo()

print f.foo  # first calculate this is result
print f.foo  # this is result

运行结果可见,first calculate只在第一次调用时候被计算之后就把结果缓存起来了。这样的好处是在网络编程中,对HTTP协议的解析,通常会把HTTP的header解析成python的一个字典,而在视图函数的时候,可能不知一次的访问这个header,因此把这个header使用描述器缓存起来,可以减少多余的解析。

描述器在python的应用十分广泛,通常是配合装饰器一起使用。强大的魔法来自强大的责任。描述器还可以用来实现ORM中对sql语句的"预编译"。恰当的使用描述器,可以让自己的Python代码更优雅。

Python 相关文章推荐
简化Python的Django框架代码的一些示例
Apr 20 Python
利用python编写一个图片主色转换的脚本
Dec 07 Python
django认证系统实现自定义权限管理的方法
Jul 16 Python
Python图像处理之图像的缩放、旋转与翻转实现方法示例
Jan 04 Python
Python 根据数据模板创建shapefile的实现
Nov 26 Python
python读取ini配置文件过程示范
Dec 23 Python
torch 中各种图像格式转换的实现方法
Dec 26 Python
详解Anaconda 的安装教程
Sep 23 Python
python pyhs2 的安装操作
Apr 07 Python
python实战之用emoji表情生成文字
May 08 Python
Python编写nmap扫描工具
Jul 21 Python
Python实现批量自动整理文件
Mar 16 Python
Python黑魔法Descriptor描述符的实例解析
Jun 02 #Python
深入理解Python变量与常量
Jun 02 #Python
Python中的Descriptor描述符学习教程
Jun 02 #Python
从源码解析Python的Flask框架中request对象的用法
Jun 02 #Python
Python搭建APNS苹果推送通知推送服务的相关模块使用指南
Jun 02 #Python
Python的Django框架中使用SQLAlchemy操作数据库的教程
Jun 02 #Python
实例解析Python中的__new__特殊方法
Jun 02 #Python
You might like
table标签的结构与合并单元格的实现方法
2013/07/24 PHP
PHP实现获取客户端IP并获取IP信息
2015/03/17 PHP
php异步:在php中使用fsockopen curl实现类似异步处理的功能方法
2016/12/10 PHP
Yii框架安装简明教程
2020/05/15 PHP
document对象execCommand的command参数介绍
2006/08/01 Javascript
关于火狐(firefox)及ie下event获取的两种方法
2012/12/27 Javascript
jQuery仿Excel表格编辑功能的实现代码
2013/05/01 Javascript
JavaScript之Object类型介绍
2015/04/01 Javascript
js中javascript:void(0) 真正含义
2020/11/05 Javascript
js console.log打印对像与数组用法详解
2016/01/21 Javascript
JS弹性运动实现方法分析
2016/12/15 Javascript
JS图片轮播与索引变色功能实例详解
2017/07/06 Javascript
Vue动态组件实例解析
2017/08/20 Javascript
集合Bootstrap自定义confirm提示效果
2017/09/19 Javascript
nodejs结合Socket.IO实现的即时通讯功能详解
2018/01/12 NodeJs
js+canvas绘制图形验证码
2020/09/21 Javascript
python打开文件并获取文件相关属性的方法
2015/04/23 Python
python中requests和https使用简单示例
2018/01/18 Python
python3爬取torrent种子链接实例
2020/01/16 Python
python GUI库图形界面开发之PyQt5打印控件QPrinter详细使用方法与实例
2020/02/28 Python
Jupyter notebook快速入门教程(推荐)
2020/05/18 Python
python实点云分割k-means(sklearn)详解
2020/05/28 Python
基于Python爬虫采集天气网实时信息
2020/06/05 Python
如何使用 Python 读取文件和照片的创建日期
2020/09/05 Python
pycharm中如何自定义设置通过“ctrl+滚轮”进行放大和缩小实现方法
2020/09/16 Python
Python实现给PDF添加水印的方法
2021/01/25 Python
德国净水壶和滤芯品牌:波尔德PearlCo(家用净水器)
2020/04/29 全球购物
老教师工作总结的自我评价
2013/09/27 职场文书
思想汇报范文
2013/11/04 职场文书
高三体育教学反思
2014/01/29 职场文书
马丁路德金演讲稿
2014/05/19 职场文书
教师廉洁自律承诺书
2014/05/26 职场文书
网上祭英烈活动总结
2015/02/04 职场文书
导游词之介休绵山
2019/12/31 职场文书
MybatisPlus代码生成器的使用方法详解
2021/06/13 Java/Android
《吸血鬼幸存者》新内容发布 追加多个全新模式
2022/04/07 其他游戏