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