正如@MSeifert 已经解释的那样,dir 调用对象类的__dir__ 属性。所以type(X).__dir__ 被调用,而不是X.__dir__。对于那些感兴趣的人,这里是幕后故事的一瞥。
dir的实现在bltinmodule.c:
builtin_dir(PyObject *self, PyObject *args)
{
PyObject *arg = NULL;
if (!PyArg_UnpackTuple(args, "dir", 0, 1, &arg))
return NULL;
return PyObject_Dir(arg);
}
dir 函数调用 API 函数 PyObject_Dir。 PyObject_Dir函数在object.c中实现:
PyObject *
PyObject_Dir(PyObject *obj)
{
return (obj == NULL) ? _dir_locals() : _dir_object(obj);
}
PyObject_Dir 是使用两个辅助函数定义的。当传入一个对象时 - 就像这里的情况一样 - 然后调用 _dir_object 函数。在object.c也实现了:
static PyObject *
_dir_object(PyObject *obj)
{
PyObject *result, *sorted;
PyObject *dirfunc = _PyObject_LookupSpecial(obj, &PyId___dir__);
assert(obj);
if (dirfunc == NULL) {
if (!PyErr_Occurred())
PyErr_SetString(PyExc_TypeError, "object does not provide __dir__");
return NULL;
}
/* use __dir__ */
result = _PyObject_CallNoArg(dirfunc);
Py_DECREF(dirfunc);
if (result == NULL)
return NULL;
/* return sorted(result) */
sorted = PySequence_List(result);
Py_DECREF(result);
if (sorted == NULL)
return NULL;
if (PyList_Sort(sorted)) {
Py_DECREF(sorted);
return NULL;
}
return sorted;
}
关注的部分是:
PyObject *dirfunc = _PyObject_LookupSpecial(obj, &PyId___dir__);
这是在传入的对象上查找__dir__ 特殊方法的地方。这是使用_PyObject_LookupSpecial 完成的。 _PyObject_LookupSpecial 在typeobject.c 中定义:
PyObject *
_PyObject_LookupSpecial(PyObject *self, _Py_Identifier *attrid)
{
PyObject *res;
res = _PyType_LookupId(Py_TYPE(self), attrid);
if (res != NULL) {
descrgetfunc f;
if ((f = Py_TYPE(res)->tp_descr_get) == NULL)
Py_INCREF(res);
else
res = f(res, self, (PyObject *)(Py_TYPE(self)));
}
return res;
}
_PyObject_LookupSpecial 首先在传入的对象上调用Py_TYPE,然后使用_PyType_LookupId 查找属性。 Py_TYPE is a macro 获取对象的 ob_type 成员。它的扩展形式是:
(((PyObject*)(o))->ob_type)
正如您可能猜到的那样,ob_type attribute is the class (or type) of the object:
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
所以,从上面可以看出,使用的__dir__ 属性确实是对象的类。