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的源码浅要剖析Python的内存管理
Apr 16 Python
Django 实现下载文件功能的示例
Mar 06 Python
从DataFrame中提取出Series或DataFrame对象的方法
Nov 10 Python
python之拟合的实现
Jul 19 Python
pytorch .detach() .detach_() 和 .data用于切断反向传播的实现
Dec 27 Python
Python3基本输入与输出操作实例分析
Feb 14 Python
基于python 等频分箱qcut问题的解决
Mar 03 Python
Python按照list dict key进行排序过程解析
Apr 04 Python
利用python为PostgreSQL的表自动添加分区
Jan 18 Python
Python中使用Selenium环境安装的方法步骤
Feb 22 Python
Python 实现定积分与二重定积分的操作
May 26 Python
Python制作一个随机抽奖小工具的实现
Jul 07 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 cookis创建实现代码
2009/03/16 PHP
解析:通过php socket并借助telnet实现简单的聊天程序
2013/06/18 PHP
php数组使用规则分析
2015/02/27 PHP
Nigma vs Alliance BO5 第五场2.14
2021/03/10 DOTA
Knockoutjs的环境搭建教程
2012/11/26 Javascript
JavaScript和CSS通过expression实现Table居中显示
2013/06/28 Javascript
js如何判断用户是在PC端和还是移动端访问
2014/04/24 Javascript
require.js深入了解 require.js特性介绍
2014/09/04 Javascript
JavaScript严格模式禁用With语句的原因
2014/10/20 Javascript
基于jquery实现智能表单验证操作
2016/05/09 Javascript
获取今天,昨天,本周,上周,本月,上月时间(实例分享)
2017/01/04 Javascript
JavaScript基础之流程控制语句的用法
2017/08/31 Javascript
js比较两个单独的数组或对象是否相等的实例代码
2019/04/28 Javascript
ES6 Object方法扩展的应用实例分析
2019/06/25 Javascript
jQuery中getJSON跨域原理的深入讲解
2020/09/02 jQuery
js实现磁性吸附的示例
2020/10/26 Javascript
Python httplib,smtplib使用方法
2008/09/06 Python
Python实现过滤单个Android程序日志脚本分享
2015/01/16 Python
python实现下载指定网址所有图片的方法
2015/08/08 Python
Python采用Django开发自己的博客系统
2020/09/29 Python
Python 基础知识之字符串处理
2017/01/06 Python
Python multiprocessing多进程原理与应用示例
2019/02/28 Python
Python HTML解析器BeautifulSoup用法实例详解【爬虫解析器】
2019/04/05 Python
docker-py 用Python调用Docker接口的方法
2019/08/30 Python
了解一下python内建模块collections
2020/09/07 Python
Ray-Ban雷朋瑞典官方网站:全球领先的太阳眼镜品牌
2019/08/22 全球购物
Timberland德国官网:靴子、鞋子、衣服、夹克及配件
2019/12/10 全球购物
Ticketmaster意大利:音乐会、节日、艺术和剧院的官方门票
2019/12/23 全球购物
贝佳斯官方网站:Borghese
2020/05/08 全球购物
老龄工作先进事迹
2014/08/15 职场文书
小学班主任自我评价
2015/03/11 职场文书
教师教育心得体会
2016/01/19 职场文书
党员干部学习心得体会
2016/01/23 职场文书
2016年小学端午节活动总结
2016/04/01 职场文书
Python文件的操作示例的详细讲解
2021/04/08 Python
Pyhton模块和包相关知识总结
2021/05/12 Python