__dict__ Attribute of Ultimate Base Class, object in Python

Viewed 802

Following piece of code lists the attributes of a class named 'A' in a sorted order:-

>>> class A():
        def __init__(self, i):
            self.at = i

>>> sorted(vars(A))
['__dict__', '__doc__', '__init__', '__module__', '__weakref__']

Now, printing the value of key, '__dict__' results this:-

>>> vars(A)['__dict__']                 #Value of '__dict__'
<attribute '__dict__' of 'A' objects>

As per docs, vars([object])

Return the __dict__ attribute for a module, class, instance, or any other object with a __dict__ attribute.


What I am not understanding is that is the '__dict__' attribute in the list the same attribute used by vars() to return the attributes of A or is it a different attribute which has some another objective like implementing A's objects' namespace as suggested (according to me) by the value which '__dict__' holds.


Edit:-

The first part of the question is very much related to this other question (also, mentioned by @eliotness) but it's the second part (described below) for which I can't find any answers or related question and hence, changing title of the question.


Let's consider another code that produces list of attributes of ultimate base class in Python, object:-

>>> sorted(vars(object))
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', ...., '__str__', '__subclasshook__']
>>> hasattr(object, '__dict__')
True
>>> sorted(getattr(object, '__dict__')) == sorted(vars(object))
True

Another quotation from docs about object.__dict__

A dictionary or other mapping object used to store an object’s (writable) attributes.


This time, '__dict__' doesn't show up in the list of object. So, is it that the __dict__ attribute is a read-only attribute in case of object or any other reason?

Also, is it possible to get a list of read-only attributes in Python in any way?


2 Answers

The first part of your question is already answered by the linked answer: the __dict__ of instances is stored as a descriptor on the class. This is the A.__dict__['__dict__']. A.__dict__ on the other hand stores all the attributes of the A class - which itself is an instance of type. So actually it's type.__dict__['__dict__'] that provides these variables:

>>> type.__dict__['__dict__']
<attribute '__dict__' of 'type' objects>
>>> A.__dict__ == type.__dict__['__dict__'].__get__(A)
True

The reason why you're not seeing a __dict__ attribute on object is because it doesn't have one. This means you can't set instance variables on object instances:

>>> o = object()
>>> o.x = 1

AttributeError: 'object' object has no attribute 'x'

Similar behavior can be achieved for custom classes by defining __slots__:

>>> class B:
...     __slots__ = ()
... 
>>> vars(B)
mappingproxy({'__module__': '__main__', '__slots__': (), '__doc__': None})
>>> 
>>> b = B()
>>> b.x = 1

AttributeError: 'B' object has no attribute 'x'

Firstly I want to say that I only partly know the answer to your question. The second part may not be entirely true on the behaviour of python. But first, I want to clear some things out: the docs you are quotings are concerning normal objects and may not apply on meta-classes as their internal behaviour is entirely coded in C.

vars(A)['dict']

As I understand how python works, I suspect the __dict__ you saw here:

>>> vars(A)['__dict__']                 #Value of '__dict__'
<attribute '__dict__' of 'A' objects>

I suspect it to be a sample for the __dict__ attribute of a future instance of the class. I don't have better answer for now but let's keep digging.

The __dict__ attribute on meta-classes

As object is a of class type is is pretty normal for it not to have a __dict__ as it is not possible to add attribute on basic types like int, float, or str. So why would these types have a __dict__ attribute if it is not used?

And finally, yes. There are restrictions on the __dict__ attribute of classes and meta-classes. These restrictions take form as the class MappingProxy like said in the vars documentation:

Objects such as modules and instances have an updateable __dict__ attribute; however, other objects may have write restrictions on their __dict__ attributes (for example, classes use a types.MappingProxyType to prevent direct dictionary updates).

But this behaviour is also explicitly said in the doc of the class object

Edit: I started to find good information about the wierd vars(A)['__dict__']. Take a look here

Related