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的线程来解决生产者消费问题的示例
Apr 02 Python
在Python中处理时间之clock()方法的使用
May 22 Python
Python字符串特性及常用字符串方法的简单笔记
Jan 04 Python
Python正则表达式使用经典实例
Jun 21 Python
Python3实现的判断环形链表算法示例
Mar 07 Python
python os.path.isfile()因参数问题判断错误的解决
Nov 29 Python
Python如何实现小程序 无限求和平均
Feb 18 Python
python利用datetime模块计算程序运行时间问题
Feb 20 Python
细数nn.BCELoss与nn.CrossEntropyLoss的区别
Feb 29 Python
python TCP包注入方式
May 05 Python
jupyter notebook快速入门及使用详解
Nov 13 Python
你喜欢篮球吗?Python实现篮球游戏
Jun 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 url路由入门实例
2014/04/23 PHP
php添加数据到xml文件的简单例子
2016/09/08 PHP
php使用 readfile() 函数设置文件大小大小的方法
2017/08/11 PHP
PHP实现数据库统计时间戳按天分组输出数据的方法
2017/10/10 PHP
PHP中的异常处理机制深入讲解
2020/11/10 PHP
Javascript 构造函数 实例分析
2008/11/26 Javascript
Prototype ObjectRange对象学习
2009/07/19 Javascript
jquery 插件学习(二)
2012/08/06 Javascript
jquery ajax 局部无刷新更新数据的实现案例
2014/02/08 Javascript
详解jQuery向动态生成的内容添加事件响应jQuery live()方法
2015/11/02 Javascript
利用Javascript仿Excel的数据透视分析功能
2016/09/07 Javascript
微信小程序 在Chrome浏览器上运行以及WebStorm的使用
2016/09/27 Javascript
微信和qq时间格式模板实例详解
2016/10/21 Javascript
JS开发中百度地图+城市联动实现实时触发查询地址功能
2017/04/13 Javascript
BootStrap Validator 根据条件在JS中添加或移除校验操作
2017/10/12 Javascript
JavaScript比较同一天的时间大小实例代码
2018/02/09 Javascript
JavaScript实现新年倒计时效果
2018/11/17 Javascript
20道JS原理题助你面试一臂之力(必看)
2019/07/22 Javascript
JavaScript监听触摸事件代码实例
2019/12/30 Javascript
python操作摄像头截图实现远程监控的例子
2014/03/25 Python
高性能web服务器框架Tornado简单实现restful接口及开发实例
2014/07/16 Python
Python+matplotlib实现填充螺旋实例
2018/01/15 Python
django用户登录验证的完整示例代码
2019/07/21 Python
python线程的几种创建方式详解
2019/08/29 Python
如何在mac环境中用python处理protobuf
2019/12/25 Python
详解Python实现进度条的4种方式
2020/01/15 Python
Jupyter打开图形界面并画出正弦函数图像实例
2020/04/24 Python
css3学习系列之移动属性详解
2017/07/04 HTML / CSS
css3实现二维码扫描特效的示例
2020/10/29 HTML / CSS
世界上最大的街头服饰网站:Karmaloop
2017/02/04 全球购物
Carrs Silver官网:英国著名的银器品牌
2020/08/29 全球购物
PHP面试题集
2016/12/18 面试题
2014年幼儿园植树节活动方案
2014/03/02 职场文书
委托书样本
2014/04/02 职场文书
2015年语文教师工作总结
2015/05/25 职场文书
Python加密与解密模块hashlib与hmac
2022/06/05 Python