Python描述器descriptor详解


Posted in Python onFebruary 03, 2015

前面说了descriptor,这个东西其实和Java的setter,getter有点像。但这个descriptor和上文中我们开始提到的函数方法这些东西有什么关系呢?

所有的函数都可以是descriptor,因为它有__get__方法。

>>> def hello():  

    pass  

>>> dir(hello)  

['__call__', '__class__', '__delattr__', '__dict__', '__doc__', '<span style="color: #ff0000;">__get__</span>  

', '__getattribute__',   

'__hash__', '__init__', '__module__', '__name__', '__new__',   

'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 'func_closure',   

'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']  

>>>  

 注意,函数对象没有__set__和__del__方法,所以它是个non-data descriptor.

方法其实也是函数,如下:

>>> class T(object):  

    def hello(self):  

        pass  

>>> T.__dict__['hello']  

<function hello at 0x00CD7EB0>  

>>> 

 或者,我们可以把方法看成特殊的函数,只是它们存在于类 中,获取函数属性时,返回的不是函数本身(比如上面的<function hello at 0x00CD7EB0>),而是返回函数的__get__方法的返回值,接着上面类T的定义:

>>> T.hello   获取T的hello属性,根据查找策略,从T的__dict__中找到了,找到的是<function hello at 0x00CD7EB0>,但不会直接返回<function hello at 0x00CD7EB0>,因为它有__get__方法,所以返回的是调用它的__get__(None, T)的结果:一个unbound方法。

<unbound method T.hello>
>>> f = T.__dict__['hello']   #直接从T的__dict__中获取hello,不会执行查找策略,直接返回了<function hello at 0x00CD7EB0>

>>> f

<function hello at 0x00CD7EB0>

>>> t = T()                 

>>> t.hello                     #从实例获取属性,返回的是调用<function hello at 0x00CD7EB0>的__get__(t, T)的结果:一个bound方法。
<bound method T.hello of <__main__.T object at 0x00CDAD10>>

>>>

 为了证实我们上面的说法,在继续下面的代码(f还是上面的<function hello at 0x00CD7EB0>):

>>> f.__get__(None, T)  

<unbound method T.hello>  

>>> f.__get__(t, T)  

<bound method T.hello of <__main__.T object at 0x00CDAD10>> 

 好极了!

总结一下:

      1.所有的函数都有__get__方法

      2.当函数位于类的__dict__中时,这个函数可以认为是个方法,通过类或实例获取该函数时,返回的不是函数本身,而是它的__get__方法返回值。

我承认我可能误导你认为方法就是函数,是特殊的函数。其实方法和函数还是有区别的,准确的说:方法就是方法,函数就是函数。

>>> type(f)  

<type 'function'>  

>>> type(t.hello)  

<type 'instancemethod'>  

>>> type(T.hello)  

<type 'instancemethod'>  

>>>  

 函数是function类型的,method是instancemethod(这是普通的实例方法,后面会提到classmethod和staticmethod)。

关于unbound method和bound method,再多说两句。在c实现中,它们是同一个对象(它们都是instancemethod类型的),我们先看看它们里面到底是什么

>>> dir(t.hello)  

['__call__', '__class__', '__cmp__', '__delattr__', '__doc__', '__get__', '__getattribute__',   

'__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',   

'__str__', 'im_class', 'im_func', 'im_self'] 

 __call__说明它们是个可调用对象,而且我们还可以猜测,这个__call__的实现应该大致是:转调另外一个函数(我们期望的哪个,比如上面的hello),并以对象作为第一参数。

要 注意的是im_class,im_func,im_self。这几个东西我们并不陌生,在t.hello里,它们分别代表T,hello(这里是存储在 T.__dict__里的函数hello)和t。有了这些我们可以大致想象如何纯Python实现一个instancemethod了:)。

其实还有几个内建函数都和descriptor有关,下面简单说说。

classmethod

classmethod能将一个函数转换成类方法,类方法的第一个隐含参数是类本身 (普通方法的第一个隐含参数是实例本身),类方法即可从类调用,也可以从实例调用(普通方法只能从实例调用)。

>>> class T(object):  

    def hello(cls):  

        print 'hello', cls  

    hello = classmethod(hello)   #两个作用:把hello装换成类方法,同时隐藏作为普通方法的hello   

>>> t = T()  

>>> t.hello()  

hello <class '__main__.T'>  

>>> T.hello()  

hello <class '__main__.T'>  

>>>  

 注意:classmethod是个类,不是函数。classmethod类有__get__方法,所以,上面的t.hello和T.hello获得实际上是classmethod的__get__方法返回值

>>> t.hello  

<bound method type.hello of <class '__main__.T'>>  

>>> type(t.hello)  

<type 'instancemethod'>  

>>> T.hello  

<bound method type.hello of <class '__main__.T'>>  

>>> type(T.hello)  

<type 'instancemethod'>  

>>>  

 从 上面可以看出,t.hello和T.hello是instancemethod类型的,而且是绑定在T上的。也就是说classmethod的 __get__方法返回了一个instancemethod对象。从前面对instancemethod的分析上,我们应该可以推断:t.hello的 im_self是T,im_class是type(T是type的实例),im_func是函数hello

>>> t.hello.im_self  

<class '__main__.T'>  

>>> t.hello.im_class  

<type 'type'>  

>>> t.hello.im_func  

<function hello at 0x011A40B0>  

>>>  

 完全一致!所以实现一个纯Python的classmethod也不难:)

staticmethod

staticmethod能将一个函数转换成静态方法,静态方法没有隐含的第一个参数。

class T(object):  

    def hello():  

        print 'hello'  

    hello = staticmethod(hello)      

>>> T.hello()   #没有隐含的第一个参数  

hello  

>>> T.hello  

<function hello at 0x011A4270>  

>>> 

 T.hello直接返回了一个函数。猜想staticmethod类的__get__方法应该是直接返回了对象本身。

还有一个property,和上面两个差不多,它是个data descriptor。

Python 相关文章推荐
举例讲解Python的Tornado框架实现数据可视化的教程
May 02 Python
Python的Django框架中TEMPLATES项的设置教程
May 29 Python
python逆向入门教程
Jan 15 Python
python文件拆分与重组实例
Dec 10 Python
Python模块的加载讲解
Jan 15 Python
机器学习实战之knn算法pandas
Jun 22 Python
python原类、类的创建过程与方法详解
Jul 19 Python
对Python 中矩阵或者数组相减的法则详解
Aug 26 Python
django 做 migrate 时 表已存在的处理方法
Aug 31 Python
python [:3] 实现提取数组中的数
Nov 27 Python
Python坐标轴操作及设置代码实例
Jun 04 Python
关于Kotlin中SAM转换的那些事
Sep 15 Python
理解Python中的With语句
Feb 02 #Python
Linux环境下MySQL-python安装过程分享
Feb 02 #Python
Python中用pycurl监控http响应时间脚本分享
Feb 02 #Python
Python列表(list)常用操作方法小结
Feb 02 #Python
Python Sleep休眠函数使用简单实例
Feb 02 #Python
Python中实现从目录中过滤出指定文件类型的文件
Feb 02 #Python
Python实现二分法算法实例
Feb 02 #Python
You might like
PHP list() 将数组中的值赋给变量的简单实例
2016/06/13 PHP
利用PHP获取访客IP、地区位置、浏览器及来源页面等信息
2017/06/27 PHP
PHP设计模式之注册树模式分析
2018/01/26 PHP
php实现的AES加密类定义与用法示例
2018/01/29 PHP
Swoole实现异步投递task任务案例详解
2019/04/02 PHP
Firebug 字幕文件JSON地址获取代码
2009/10/28 Javascript
js二维数组排序的简单示例代码
2014/01/24 Javascript
jquery.ajax之beforeSend方法使用介绍
2014/12/08 Javascript
基于jquery和svg实现超炫酷的动画特效
2014/12/09 Javascript
vueJS简单的点击显示与隐藏的效果【实现代码】
2016/05/03 Javascript
JavaScript动态添加事件之事件委托
2016/07/12 Javascript
bootstrapValidator表单验证插件学习
2016/12/30 Javascript
mpvue网易云短信接口实现小程序短信登录的示例代码
2020/04/03 Javascript
vue开发chrome插件,实现获取界面数据和保存到数据库功能
2020/12/01 Vue.js
[01:03:51]2018DOTA2亚洲邀请赛 4.7 淘汰赛 VP vs LGD 第三场
2018/04/09 DOTA
Python删除windows垃圾文件的方法
2015/07/14 Python
matplotlib在python上绘制3D散点图实例详解
2017/12/09 Python
Python实现识别图片内容的方法分析
2018/07/11 Python
利用django+wechat-python-sdk 创建微信服务器接入的方法
2019/02/20 Python
python 实现查找文件并输出满足某一条件的数据项方法
2019/06/12 Python
Python 项目转化为so文件实例
2019/12/23 Python
Python如何使用字符打印照片
2020/01/03 Python
python 函数嵌套及多函数共同运行知识点讲解
2020/03/03 Python
浅析关于Keras的安装(pycharm)和初步理解
2020/10/23 Python
ColourPop美国官网:卡拉泡泡,洛杉矶彩妆品牌
2019/04/28 全球购物
LN-CC日本:高端男装和女装的奢侈时尚目的地
2019/09/01 全球购物
意大利领先的奢侈品在线时装零售商:MCLABELS
2020/10/13 全球购物
说说在weblogic中开发消息Bean时的persistent与non-persisten的差别
2013/04/07 面试题
生产副总岗位职责
2013/11/28 职场文书
商场中秋节广播稿
2014/01/17 职场文书
关于读书的演讲稿
2014/05/07 职场文书
五好文明家庭事迹材料
2014/12/20 职场文书
教师求职信怎么写
2015/03/20 职场文书
2016教师学习教育法心得体会
2016/01/19 职场文书
教你做个可爱的css滑动导航条
2021/06/15 HTML / CSS
Python极值整数的边界探讨分析
2021/09/15 Python