10
>>> a = object() >>> a.x = 5 Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'object' object has no attribute 'x' >>> b = lambda:0 >>> b.x = 5 >>> b.x 5 

Why do instances of the object class not have a __dict__, causing it to behave as semantically immutable? What were the reasons for choosing this design?

Specifically, why:

instances of types defined in C don't have a __dict__ attribute by default.

As noted in this question.

1

2 Answers 2

7

The documentation for Python 2 is not very helpful in giving an explanation as to why you cannot assign attributes to an object(), but the documentation for Python 3 provides a bit more information:

Return a new featureless object. object is a base for all classes. It has the methods that are common to all instances of Python classes. This function does not accept any arguments.

Note: object does not have a __dict__, so you can’t assign arbitrary attributes to an instance of the object class.

Thus, the reason you cannot add arbitrary attributes to your object() appears to be because of the fact that object() instances do not have an implementation of the __dict__ attribute, not because object() instances are immutable:

>>> hasattr(object(), '__dict__') False >>> 

Another interesting thing, but perhaps not relevant to the discussion at hand, is that while an instance of object may not have a __dict__ implementation, the object class itself does:

>>> hasattr(object, '__dict__') True 

As for the why part of the question, I cannot find any exact reasons for why object() doesn't have a __dict__. Is is probably because - as @tdelany has already mentioned on in the comments - an implementation detail. If you really want a definitive answer, you should ask Guido himself.

Sign up to request clarification or add additional context in comments.

8 Comments

@SeanPianka No. But I'm still looking. I thought it would be useful to show why you cannot add attributes to an object().
Because it is a builtin class.
object is implemented in C and its common that extension objects don't take extra attributes. Considering that regular class instance __dict__ is an object, its not surprising the object doesnt have one!
@SeanPianka Like I said, I'm still workin' on it. Gimme some time :)
@leaf - here's a good place: hg.python.org/cpython/file/tip/Include/object.h. It will show you the details of the implementation.
|
3

Because allowing arbitrary attributes would create an insane amount of memory bloat

The reason object instances don't have a __dict__ is that, if any class in a class hierarchy has a __dict__, then all instances of the subclasses have it (otherwise, the subclasses wouldn't be compatible with the parent classes; the initializer for the parent almost certainly tries to set attributes that need __dict__ after all), even if they intended to opt-out.

object is the parent class for all classes in Python, so if it had a __dict__ to hold arbitrary attributes, every class in Python would have it. And that would make literally every instance of every object in Python consume another 4-8 bytes just for the pointer, even if Python didn't create an actual dict there until someone tried to auto-vivify an attribute. On a 64 bit build, that would mean every int increases its memory consumption from 28 (higher for large numbers) bytes to 36. Every float from 24 to 32. And confusingly, every dict would have to provide __dict__.

Worse, for performance/memory consumption on the CPython reference interpreter, is that if every object can contain __dict__, then every object can potentially form part of a reference cycle. The cyclic garbage collection can normally ignore "leaf" objects (any object that can't contain another arbitrary object), so it doesn't track ints, floats, str, bytes, etc. If everything has a __dict__, then everything can be part of a reference cycle. And that means it secretly allocates two more pointers (8-16 more bytes per instance) to put it in a linked list with all the other objects for the garbage collector to track, and that means the cyclic collector has to traverse it each time its generation is collected, slowing collection. Between those two changes alone, if object provided __dict__ literally every float object in the program would double in size. Smallish ints would nearly double. Python isn't a super-memory efficient language, but adding >50% overhead for common objects just on the chance someone might want to attach arbitrary attributes to them (a ridiculously rare need) is a terrible idea.

And that's before we even get into the problem of hashability; Python hashable built-ins are immutable. If everything has a __dict__, then nothing is truly immutable, and either the hashing and equality functions have to ignore those arbitrary attributes (possible, but potentially confusing), or you have to hope users never modify an object inserted into a set or used as a dict key (because it could retroactively invalidate objects used for that purpose).

When you want to have a cheap object that can accept arbitrary attribute

object() doesn't provide __dict__ to avoid this issue. If you want a simple namespace to assign attributes to without defining your own class, types.SimpleNamespace provides this functionality (along with providing a useful repr that object wouldn't provide).

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.