python descript_Python descriptor

本文详细解析了Python中如何通过descriptor影响属性查找顺序,以实例说明了新式类中函数如何转变为方法。重点讨论了`PyFunction_Type`作为nondescriptor的作用,以及`func_descr_get`函数在绑定过程中的关键作用。
摘要由CSDN通过智能技术生成

定义: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)=cls

if hasattr(cls, 'f'):

desc = cls.f

type = 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 descriptor

if 'f' in obj.__dict__:

return obj.__dict__['f']

if hasattr(type, '__get__'):

return type.__get__(desc, obj, 'f')

#instance's __dict__ can't found

if '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)。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值