How to override the __dir__ method for a class?

I want to change the output of dir() for my class. Usually for all other objects, this is done by defining a native __dir__ method in its class. But if I do this for my class, it will not be called.

 class X(object): def __dir__(): raise Exception("No!") >>>dir(X) ['__class__', '__delattr__', '__dict__',.... 

How can I change the dir() output for a class?

+4
python class metaprogramming
source share
2 answers

This is because dir calls the __dir__ input type (equivalent to: type(inp).__dir__(inp) ). For class instances, it will call the __dir__ classes, but if it is called in the class, it will call the __dir__ metaclass.

 class X(object): def __dir__(self): # missing self parameter raise Exception("No!") dir(X()) # instance! # Exception: No! 

So, if you want to configure dir for your class (and not instances of your class), you need to add a metaclass for X :

 import six class DirMeta(type): def __dir__(cls): raise Exception("No!") @six.add_metaclass(DirMeta) class X(object): pass dir(X) # Exception: No! 
+7
source share

As @MSeifert already explained, dir calls the __dir__ attrbiute of the object class. Therefore, type(X).__dir__ is called, not X.__dir__ . Just for those who are interested, look at what really happens.

The dir implementation is in 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); } 

The dir function calls the PyObject_Dir API PyObject_Dir . The PyObject_Dir function PyObject_Dir implemented in object.c :

 PyObject * PyObject_Dir(PyObject *obj) { return (obj == NULL) ? _dir_locals() : _dir_object(obj); } 

PyObject_Dir defined using two helper functions. When an object is passed - as here - then the _dir_object function is _dir_object . It is also implemented in 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; } 

This part has focused on:

 PyObject *dirfunc = _PyObject_LookupSpecial(obj, &PyId___dir__); 

The special __dir__ method for the passed object is used here. This is done using _PyObject_LookupSpecial . _PyObject_LookupSpecial defined in 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 first calls the Py_TYPE object that passed through it before searching for the attribute using _PyType_LookupId . Py_TYPE is a macro that receives a member of ob_type objects. This is an extended form:

 (((PyObject*)(o))->ob_type) 

And as you probably guessed, the 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; 

So, as you can see above, the __dir__ attribute __dir__ indeed a class of objects.

+2
source share

All Articles