Data model and attribute resolution in subclasses

Adam Preble adam.preble at gmail.com
Sun Mar 1 04:49:30 EST 2020


Based on what I was seeing here, I did some experiments to try to understand better what is going on:

class BaseClass:
    def __init__(self):
        self.a = 1

    def base_method(self):
        return self.a

    def another_base_method(self):
        return self.a + 1


class SubClass(BaseClass):
    def __init__(self):
        super().__init__()
        self.b = 2


c = SubClass()
print(c.__dict__)
print(c.__class__.__dict__)
print(c.__class__.__subclasses__())
print(c.__class__.mro())
print(c.__class__.mro()[1].__dict__)
print(getattr(c, "base_method"))
print(c.b)
print(c.a)

With some notes:
print(c.__dict__)
{'a': 1, 'b': 2}
So the instance directly has a. I am guessing that the object's own dictionary is directly getting these are both __init__'s are run.

print(c.__class__.__dict__)
{'__module__': '__main__', '__init__': <function SubClass.__init__ at 0x000001BEEF46E488>, '__doc__': None}
I am guessing this is what is found and stuffed into the class' namespace when the class is built; that's specifically the BUILD_CLASS opcode doing its thing.

print(c.__class__.__subclasses__())
[]
What?! Why isn't this [<class '__main__.BaseClass'>]?

print(c.__class__.mro())
[<class '__main__.SubClass'>, <class '__main__.BaseClass'>, <class 'object'>]
This is more like what I expected to find with subclasses. Okay, no, method resolution order is showing the entire order.

print(c.__class__.mro()[1].__dict__)
{'__module__': '__main__', '__init__': <function BaseClass.__init__ at 0x000001BEEF46E2F0>, 'base_method': <function BaseClass.base_method at 0x000001BEEF46E378>, 'another_base_method': <function BaseClass.another_base_method at 0x000001BEEF46E400>, '__dict__': <attribute '__dict__' of 'BaseClass' objects>, '__weakref__': <attribute '__weakref__' of 'BaseClass' objects>, '__doc__': None}
No instance-level stuff. Looks like it's the base class namespace when the BUILD_CLASS opcode saw it. Okay, looking good.

print(getattr(c, "base_method"))
<bound method BaseClass.base_method of <__main__.SubClass object at 0x000001BEEF2D9898>>
I'm guessing here it didn't find it in the object's __dict__ nor the class' __dict__ so it went in mro and found it in BaseClass.

So I need a __dict__ for the class based on the code defined for it when the class is defined. That's associated with the class. I need another dictionary for each instance. That will get stuffed with whatever started getting dumped into it in __init__ (and possibly elsewhere afterwards).

What __dict__ actually is can vary. The mappingproxy helps make sure that strings are given as keys (among other things?).


More information about the Python-list mailing list