CPython内部相关部分刚刚没有实现。这可能被认为是一个错误,尽管我不知道Python对这种情况的适当描述符处理做出了什么承诺。
我可以准确解释内部会发生什么,但是由于这里有多层描述符处理,事情会变得混乱。
对于用Python实现一个__set__
或__delete__
,CPython的内部使用slot_tp_descr_set
在C级包裹它。 (是,对于那些方法的一个C函数。)
static int
slot_tp_descr_set(PyObject *self, PyObject *target, PyObject *value)
{
PyObject *res;
_Py_IDENTIFIER(__delete__);
_Py_IDENTIFIER(__set__);
if (value == NULL)
res = call_method(self, &PyId___delete__, "(O)", target);
else
res = call_method(self, &PyId___set__, "(OO)", target, value);
if (res == NULL)
return -1;
Py_DECREF(res);
return 0;
}
这使用call_method
,它绕过__getattribute__
,__getattr__
,和实例字典,而是执行描述符处理像一个正常的属性查找。
注意,有描述的两级处理在这里 - 我们在处理MyClass.d
描述符中间,但现在我们需要考虑MyClass.d
描述符的__set__
或__delete__
方法是否是自己的描述。它们不是,但是如果它们是用常规的Python函数实现的,它们将是描述符,并且Python函数的描述符处理将绑定Descriptor
实例作为其__set__
或__delete__
方法的第一个参数。
对于用Python实现一个__get__
,CPython的内部使用slot_tp_descr_get
,后者采用不同执行特殊方法查找。
static PyObject *
slot_tp_descr_get(PyObject *self, PyObject *obj, PyObject *type)
{
PyTypeObject *tp = Py_TYPE(self);
PyObject *get;
_Py_IDENTIFIER(__get__);
get = _PyType_LookupId(tp, &PyId___get__);
if (get == NULL) {
/* Avoid further slowdowns */
if (tp->tp_descr_get == slot_tp_descr_get)
tp->tp_descr_get = NULL;
Py_INCREF(self);
return self;
}
if (obj == NULL)
obj = Py_None;
if (type == NULL)
type = Py_None;
return PyObject_CallFunctionObjArgs(get, self, obj, type, NULL);
}
这里,CPython的使用_PyType_LookupId
查找__get__
上type(mc)
,而是采用call_method
来看看它在mc
。
与call_method
不同,_PyType_LookupId
没有描述符处理。 Python假定没有检查,由于它跳过描述符处理,它需要手动绑定self
。它明确地将self
(它是Descriptor
实例)传递给方法PyObject_CallFunctionObjArgs(get, self, obj, type, NULL)
。
__get__
看到Descriptor
实例作为first
因为Python使用了二级描述符坏快捷调用__get__
当内部处理,但不是要求__set__
或__delete__
时。
伟大的描述。据我所知,这不是一个规范,但我可能是错的。不管查找方法如何,这里的不一致似乎并不合适。 – rmorshea
查看[bpo-30469](http://bugs.python.org/issue30469)。 – eryksun