The Python Oracle

How different is type.__setattr__ from object.__setattr__?

--------------------------------------------------
Hire the world's top talent on demand or became one of them at Toptal: https://topt.al/25cXVn
and get $2,000 discount on your first invoice
--------------------------------------------------

Music by Eric Matyas
https://www.soundimage.org
Track title: Secret Catacombs

--

Chapters
00:00 How Different Is Type.__setattr__ From Object.__setattr__?
03:19 Accepted Answer Score 8
05:17 Thank you

--

Full question
https://stackoverflow.com/questions/4373...

--

Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...

--

Tags
#python #metaclass

#avk47



ACCEPTED ANSWER

Score 8


type.__setattr__ has a check to prevent setting attributes on types like int, and it does a bunch of invisible cleanup that isn't needed for normal objects.


Let's take a look under the hood! Here's type.__setattr__:

static int
type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
{
    if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
        PyErr_Format(
            PyExc_TypeError,
            "can't set attributes of built-in/extension type '%s'",
            type->tp_name);
        return -1;
    }
    if (PyObject_GenericSetAttr((PyObject *)type, name, value) < 0)
        return -1;
    return update_slot(type, name);
}

and if we examine PyBaseObject_Type, we see it uses PyObject_GenericSetAttr for its __setattr__, the same call that appears halfway through type_setattro.

Thus, type.__setattr__ is like object.__setattr__, but with some additional handling wrapped around it.

First, the if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) check prohibits attribute assignment on types written in C, like int or numpy.array, because assigning attributes on those can seriously screw up the Python internals in ways someone unfamiliar with the C API might not expect.

Second, after the PyObject_GenericSetAttr call updates the type's dict or calls an appropriate descriptor from the metaclass, update_slot fixes up any slots affected by the attribute assignment. These slots are C-level function pointers that implement functionality like instance allocation, in checks, +, deallocation, etc. Most of them have corresponding Python-level methods, like __contains__ or __add__, and if one of those Python-level methods is reassigned, the corresponding slot (or slots) have to be updated, too. update_slot also updates slots on all descendants of the class, and it invalidates entries in an internal attribute cache used for type object attributes.