我想更改班级的dir()
输出。通常,对于所有其他对象,通过在类中定义自己的__dir__
方法来完成。但如果我为我的班级做这件事,那就不会被召唤。
class X(object):
def __dir__():
raise Exception("No!")
>>>dir(X)
['__class__', '__delattr__', '__dict__',....
如何更改课程的dir()
输出?
答案 0 :(得分:9)
这是因为dir
调用了输入类型的__dir__
(相当于:type(inp).__dir__(inp)
)。对于类的实例,它将调用类__dir__
但如果在类上调用它将调用元类的__dir__
。
class X(object):
def __dir__(self): # missing self parameter
raise Exception("No!")
dir(X()) # instance!
# Exception: No!
因此,如果您要为自己的班级(不是班级实例)自定义dir
,则需要为X
添加元类:
import six
class DirMeta(type):
def __dir__(cls):
raise Exception("No!")
@six.add_metaclass(DirMeta)
class X(object):
pass
dir(X)
# Exception: No!
答案 1 :(得分:2)
正如@MSeifert已经解释的那样,dir
会调用对象类的__dir__
attrbiute。因此调用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
中定义了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
,然后使用ob_type
查找属性。 typeobject.c获取对象的(((PyObject*)(o))->ob_type)
成员。它的扩展形式是:
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
正如您可能猜到的那样,Py_TYPE
is a macro:
__dir__
因此,正如您从上面所看到的,使用的userService.name === service.name && !user.disabled
属性确实是对象的类。