博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python descriptor
阅读量:6938 次
发布时间:2019-06-27

本文共 3320 字,大约阅读时间需要 11 分钟。

hot3.png

定义:descriptor是一个对象属性,拥有下述的任何一个绑定方法(__set__, __get__, __delete__)。

协议:

#descriptor are only invoked for new style objects or classes.class descriptor(object):    def __set__(self, obj, value):        pass    def __get__(self, obj, cls):        pass    def __delete__(self, obj):        pass

类型: data descriptor: 定义了 __set__ , __get__ 方法。

           non data descriptor: 定义了__get__, 未定义__set__。

 descriptor 会影响对象属性的查找顺序。总结如下:

1. instance 属性 优先于class 属性

2. 如果在class属性中发现同名的data descriptor, 那么该data descriptor会优于instance属性

附上一个属性查找逻辑代码(get):

#search an attribute 'f' of obj, type(obj)=clsif hasattr(cls, 'f'):    desc = cls.ftype = descriptor.__class__if hasattr(type, '__get__') and hasattr(type, '__set__') or 'f' not in obj.__dict__:    return type.__get__(desc, obj, 'f')#can't found through descriptorif 'f' in obj.__dict__:    return obj.__dict__['f']if hasattr(type, '__get__'):    return type.__get__(desc, obj, 'f')#instance's __dict__ can't foundif 'f' in obj.__class__.__dict__:    return obj.__class__.__dict__['f']#search in base classes by mro

下面我们来看一个非常有意思的示例,此例也说明了new style class 方法实现绑定的机制。

>>> class A:	def fun(self):		pass>>> a = A()>>> type(A.fun), type(a.fun)(
,
)

 上图可以发现A.fun是普通的python函数对象(PyFunction_Type),而A的实例的fun(a.fun)居然变成了PyMethod_Type。

为什么会变成这样,他们之间有什么关系?下面我们慢慢道来,查看一下python源码会发现PyFunction_Type的定义如下:

PyTypeObject PyFunction_Type = {    PyVarObject_HEAD_INIT(&PyType_Type, 0)    "function",    sizeof(PyFunctionObject),    0,    (destructor)func_dealloc,                   /* tp_dealloc */    ...    func_descr_get,                             /* tp_descr_get */    0,                                          /* tp_descr_set */    offsetof(PyFunctionObject, func_dict),      /* tp_dictoffset */    0,                                          /* tp_init */    0,                                          /* tp_alloc */    func_new,                                   /* tp_new */};

可以看到类型定义里有2个特殊标记的函数指针,实际上他们分别对应着__get__,__set__的实现。至此我们明白了原来这个

PyFunction_Type的实例其实就是一个non data descriptor,对于a.fun,由于a的dict中并没有fun的属性,所以到A的dict中查找,

由于fun是一个non data descriptor属性,所以A.fun相当于 A.fun.__get__(a, A)。

下面我们看一下 func_descr_get 的实现:

/* Bind a function to an object */static PyObject *func_descr_get(PyObject *func, PyObject *obj, PyObject *type){    if (obj == Py_None || obj == NULL) {        Py_INCREF(func);        return func;    }    return PyMethod_New(func, obj);}
PyObject *PyMethod_New(PyObject *func, PyObject *self){    register PyMethodObject *im;    if (self == NULL) {        PyErr_BadInternalCall();        return NULL;    }    im = free_list;    if (im != NULL) {        free_list = (PyMethodObject *)(im->im_self);        PyObject_INIT(im, &PyMethod_Type);        numfree--;    }    else {        im = PyObject_GC_New(PyMethodObject, &PyMethod_Type);        if (im == NULL)            return NULL;    }    im->im_weakreflist = NULL;    Py_INCREF(func);    im->im_func = func;    Py_XINCREF(self);    im->im_self = self;    _PyObject_GC_TRACK(im);    return (PyObject *)im;}

最终descriptor会返回一个PyMethod_Type的一个instance。实际上这个obj就是fun声明时的self即(a),这里你应该也明白

class 方法声明时的那个self位置参数了吧。上面这个函数变身的过程也就是属性方法的绑定。每次调用是都会进行绑定,

创建新的PyMethod_Type对象,虽然python是用了对象缓存机制,但还是不可避免的产生性能损失,对于一个频繁使用的

方法,建议大家使用unbound method版本,即A.fun(a)。

新手,如有不对还请大家指正。轻拍!

转载于:https://my.oschina.net/u/140191/blog/36741

你可能感兴趣的文章
jsp中把js变量赋给java变量,或者将java变量赋给js变量怎么做
查看>>
Spring Shiro
查看>>
递归求组合数
查看>>
小蚂蚁学习数据结构(10)——树的基本介绍
查看>>
域环境迁移
查看>>
FastDFS安装使用实战一(安装篇)
查看>>
我的友情链接
查看>>
更改DEB包内容
查看>>
我的友情链接
查看>>
ibatis动态语句中的prepend
查看>>
keepalived实现LVS的高可用以及实现web服务的高可用(主从模型、双主模型)
查看>>
linux apache
查看>>
Mysql DBA 高级运维学习之路-删除表中数据
查看>>
4.26日第14次作业,23章项目整体绩效评估,24-32章信息安全相关知识
查看>>
新一代java模板引擎典范 Beetl
查看>>
centos6.8+nginx搭建简单的https服务器
查看>>
cut,sort,wc,uniq,tee,tr,split,并且,和,或者
查看>>
LVS负载均衡之三:LVS-DR搭建web群集、LVS结合Keepalived搭建高可用web群集
查看>>
JavaScript 堆内存分析新工具 OneHeap
查看>>
浅谈java异常机制
查看>>