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图算法实例分析
Aug 13 Python
快速了解Python相对导入
Jan 12 Python
浅谈Python基础—判断和循环
Mar 22 Python
python 判断字符串中是否含有汉字或非汉字的实例
Jul 15 Python
python实现两张图片拼接为一张图片并保存
Jul 16 Python
Python re 模块findall() 函数返回值展现方式解析
Aug 09 Python
django中瀑布流写法实例代码
Oct 14 Python
python实现连续变量最优分箱详解--CART算法
Nov 22 Python
python中wheel的用法整理
Jun 15 Python
Django启动时找不到mysqlclient问题解决方案
Nov 11 Python
Python selenium的这三种等待方式一定要会!
Jun 10 Python
Pandas数据类型之category的用法
Jun 28 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
摩卡咖啡
2021/03/03 咖啡文化
PHP实现的功能是显示8条基色色带
2006/10/09 PHP
基于PHP导出Excel的小经验 完美解决乱码问题
2013/06/10 PHP
php读取excel文件的简单实例
2013/08/26 PHP
简单的php新闻发布系统教程
2014/05/09 PHP
PHP传参之传值与传址的区别
2015/04/24 PHP
php筛选不存在的图片资源
2015/04/28 PHP
一个简单安全的PHP验证码类、PHP验证码
2016/09/24 PHP
jQuery实现文本框邮箱输入自动补全效果
2015/11/17 Javascript
实例详解JSON数据格式及json格式数据域字符串相互转换
2016/01/07 Javascript
js获取对象、数组的实际长度,元素实际个数的实现代码
2016/06/08 Javascript
浅谈js多维数组和hash数组定义和使用
2016/07/27 Javascript
DOM 事件的深入浅出(一)
2016/12/05 Javascript
JavaScript实现多叉树的递归遍历和非递归遍历算法操作示例
2018/02/08 Javascript
JS实现监控微信小程序的原理
2018/06/15 Javascript
解决vue router组件状态刷新消失的问题
2018/08/01 Javascript
微信小程序开发之tabbar图标和颜色的实现
2018/10/17 Javascript
微信小程序canvas动态时钟
2020/10/22 Javascript
[41:52]DOTA2-DPC中国联赛 正赛 CDEC vs Dynasty BO3 第二场 2月22日
2021/03/11 DOTA
基于python yield机制的异步操作同步化编程模型
2016/03/18 Python
Python中的错误和异常处理简单操作示例【try-except用法】
2017/07/25 Python
python下的opencv画矩形和文字注释的实现方法
2019/07/09 Python
Windows 下python3.8环境安装教程图文详解
2020/03/11 Python
浅谈keras 的抽象后端(from keras import backend as K)
2020/06/16 Python
python时间序列数据转为timestamp格式的方法
2020/08/03 Python
CSS3实现缺角矩形,折角矩形以及缺角边框
2019/12/20 HTML / CSS
路政管理专业个人自荐信范文
2013/11/30 职场文书
读书心得体会
2013/12/28 职场文书
2014和解协议书范文
2014/09/15 职场文书
学习党章的体会
2014/11/07 职场文书
如何有效防止sql注入的方法
2021/05/25 SQL Server
如何在Mac上通过docker配置PHP开发环境
2021/05/29 PHP
使用Redis做预定库存缓存功能
2022/04/02 Redis
vue-cli3.0修改打包后的文件名和文件地址,打包后本地运行报错解决
2022/04/06 Vue.js
golang用type-switch判断interface的实际存储类型
2022/04/14 Golang
Python尝试实现蒙特卡罗模拟期权定价
2022/04/21 Python