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查找相似单词的方法
Mar 05 Python
使用简单工厂模式来进行Python的设计模式编程
Mar 01 Python
pandas or sql计算前后两行数据间的增值方法
Apr 20 Python
Python matplotlib画图与中文设置操作实例分析
Apr 23 Python
Django admin.py 在修改/添加表单界面显示额外字段的方法
Aug 22 Python
python logging日志模块原理及操作解析
Oct 12 Python
python3 BeautifulSoup模块使用字典的方法抓取a标签内的数据示例
Nov 28 Python
解决c++调用python中文乱码问题
Jul 29 Python
Python+pyftpdlib实现局域网文件互传
Aug 24 Python
python判断字符串以什么结尾的实例方法
Sep 18 Python
如何在Python3中使用telnetlib模块连接网络设备
Sep 21 Python
python爬虫scrapy基于CrawlSpider类的全站数据爬取示例解析
Feb 20 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
探讨方法的重写(覆载)详解
2013/06/08 PHP
php检查函数必传参数是否存在的实例详解
2017/08/28 PHP
php获取微信共享收货地址的方法
2017/12/21 PHP
Yii框架分页技术实例分析
2019/08/30 PHP
在Laravel 中实现是否关注的示例
2019/10/22 PHP
js前台判断开始时间是否小于结束时间
2012/02/23 Javascript
我的Node.js学习之路(三)--node.js作用、回调、同步和异步代码 以及事件循环
2014/07/06 Javascript
JS动态加载当前时间的方法
2015/02/09 Javascript
JS创建对象几种不同方法详解
2016/03/01 Javascript
jQuery学习心得总结(必看篇)
2016/06/10 Javascript
AngularJS过滤器filter用法总结
2016/12/13 Javascript
js实现复选框的全选和取消全选效果
2017/01/03 Javascript
微信JSAPI Ticket接口签名详解
2020/06/28 Javascript
基于Node.js模板引擎教程-jade速学与实战1
2017/09/17 Javascript
angular2实现统一的http请求头方法
2018/08/13 Javascript
微信小程序上传文件到阿里OSS教程
2019/05/20 Javascript
jQuery实现的图片点击放大缩小功能案例
2020/01/02 jQuery
Element-ui树形控件el-tree自定义增删改和局部刷新及懒加载操作
2020/08/31 Javascript
用Python展示动态规则法用以解决重叠子问题的示例
2015/04/02 Python
Python模拟三级菜单效果
2017/09/11 Python
Python实现的远程登录windows系统功能示例
2018/06/21 Python
在Python中合并字典模块ChainMap的隐藏坑【推荐】
2019/06/27 Python
python 返回一个列表中第二大的数方法
2019/07/09 Python
django迁移数据库错误问题解决
2019/07/29 Python
numpy数组做图片拼接的实现(concatenate、vstack、hstack)
2019/11/08 Python
Transpose 数组行列转置的限制方式
2020/02/11 Python
Python面向对象魔法方法和单例模块代码实例
2020/03/25 Python
python实现五子棋程序
2020/04/24 Python
Python requests模块安装及使用教程图解
2020/06/30 Python
如何在Oracle中查看各个表、表空间占用空间的大小
2015/10/31 面试题
在weblogic中发布ejb需涉及到哪些配置文件
2012/01/17 面试题
就业推荐自我鉴定
2013/10/06 职场文书
项目资料员岗位职责
2013/12/10 职场文书
毕业论文致谢词
2015/05/14 职场文书
anaconda python3.8安装后降级
2021/06/11 Python
Java框架入门之简单介绍SpringBoot框架
2021/06/18 Java/Android