Differences in __dict__ between classes with and without an explicit base class
<p>Why does a class that (implicitly) derives from <code>object</code> have more items in its <code>__dict__</code> attribute than a class that has an explicit base class? (python 3.5).</p>

<pre><code>class X:
    pass
class Y(X):
    pass
X.__dict__
'''
mappingproxy({'__dict__': &lt;attribute '__dict__' of 'X' objects&gt;,
              '__doc__': None,
              '__module__': '__main__',
              '__weakref__': &lt;attribute '__weakref__' of 'X' objects&gt;})
'''

Y.__dict__
'''
mappingproxy({'__doc__': None, '__module__': '__main__'})
'''
</code></pre>

<p>The definition of <code>__weakref__</code> and <code>__dict__</code> is so the appropriate descriptor gets invoked to access the "real" location of the weak reference list and the instance dictionary of instances of the class when Python level code looks for it. The base class <code>object</code> is bare bones, and does not reserve space for <code>__weakref__</code> or <code>__dict__</code> on instances (<code>weakref.ref(object())</code> will fail, as will <code>setattr(object(), 'foo', 0)</code>).</p>

<p>For a user-defined class, it needs to define descriptors that can find those values (in CPython, these accessors are usually bypassed because there are direct pointers at the C layer, but if you explicitly reference <code>instance.__dict__</code>, it needs to know how to find it through the instance's class). The first child of <code>object</code> (without <code>__slots__</code>) needs to define these descriptors, so they can be found. The subclasses don't need to, because the location of <code>__dict__</code> and <code>__weakref__</code> doesn't change; they can continue to use the accessor for the base class, since their instances have those attributes at the same relative offset.</p>

<p>Basically, the first non-<code>__slot__</code>-ed user defined class is creating an idea of an instance as a C struct of the form:</p>

<pre><code>struct InstanceObject {
    ... common object stuff ...
    ... anything defined by __slots__ in super class(es), if anything ...
    PyObject *location_of___dict__;
    PyObject *location_of___weakref__;
}
</code></pre>

<p>and the accessors are implemented to check the type to determine the offset of those <code>location*</code> struct members, then retrieve them. The <code>struct</code> definition of a child class doesn't change the offset of those <code>location*</code> members (if new <code>__slots__</code> are added, they're just appended), so it can reuse the same accessor from the parent.</p>

python python-3.x