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中logging模块的用法实例
Sep 29 Python
TensorFlow安装及jupyter notebook配置方法
Sep 08 Python
python利用requests库进行接口测试的方法详解
Jul 06 Python
python随机数分布random测试
Aug 27 Python
Python数据类型之List列表实例详解
May 08 Python
解决Django Static内容不能加载显示的问题
Jul 28 Python
Django项目使用ckeditor详解(不使用admin)
Dec 17 Python
tensorflow ckpt模型和pb模型获取节点名称,及ckpt转pb模型实例
Jan 21 Python
keras实现VGG16方式(预测一张图片)
Jul 07 Python
Python Map 函数的使用
Aug 28 Python
JAVA SpringMVC实现自定义拦截器
Mar 16 Python
Python Pandas解析读写 CSV 文件
Apr 11 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
php获取301跳转URL简单实例
2013/12/16 PHP
php计算两个文件相对路径的方法
2015/03/14 PHP
Laravel 框架返回状态拦截代码
2019/10/18 PHP
脚本吧 - 幻宇工作室用到js,超强推荐base.js
2006/12/23 Javascript
Javascript 篱式条件判断
2008/08/22 Javascript
读jQuery之六 缓存数据功能介绍
2011/06/21 Javascript
JS仿flash上传头像效果实现代码
2011/07/18 Javascript
JavaScript/Js脚本处理html元素的自定义属性解析(亲测兼容Firefox与IE)
2013/11/25 Javascript
javascript得到当前页的来路即前一页地址的方法
2014/02/18 Javascript
IE9+已经不对document.createElement向下兼容的解决方法
2015/09/14 Javascript
微信小程序开发之相册选择和拍照详解及实例代码
2017/02/22 Javascript
详解vue.js之绑定class和style的示例代码
2017/08/24 Javascript
详解nodejs通过代理(proxy)发送http请求(request)
2017/09/22 NodeJs
Node.js中的child_process模块详解
2018/06/08 Javascript
记一次Vue.js混入mixin的使用(分权限管理页面)
2019/04/17 Javascript
layer.open的自适应及居中及子页面标题的修改方法
2019/09/05 Javascript
vue输入节流,避免实时请求接口的实例代码
2019/10/30 Javascript
JavaScript enum枚举类型定义及使用方法
2020/05/15 Javascript
JavaScript组合模式---引入案例分析
2020/05/23 Javascript
Python2.x利用commands模块执行Linux shell命令
2016/03/11 Python
Python实现螺旋矩阵的填充算法示例
2017/12/28 Python
python批量查询、汉字去重处理CSV文件
2018/05/31 Python
Scrapy-Redis结合POST请求获取数据的方法示例
2019/05/07 Python
Python实现操纵控制windows注册表的方法分析
2019/05/24 Python
python 图像处理画一个正弦函数代码实例
2019/09/10 Python
django框架两个使用模板实例
2019/12/11 Python
Python Django2 model 查询介绍(条件、范围、模糊查询)
2020/03/16 Python
html5/css3响应式页面开发总结
2018/10/16 HTML / CSS
HTML5 移动页面自适应手机屏幕四类方法总结
2017/08/17 HTML / CSS
财务管理职业生涯规划范文
2013/12/27 职场文书
学生打架检讨书
2014/02/14 职场文书
班委竞选演讲稿
2014/04/28 职场文书
政府绩效管理实施方案
2014/05/04 职场文书
精神病医院见习报告
2014/11/03 职场文书
2015年幼儿园保育员工作总结
2015/04/23 职场文书
python 中的@运算符使用
2021/05/26 Python