How does the super type present itself and do lookups?

Adam Preble adam.preble at gmail.com
Wed Mar 4 12:12:53 EST 2020


Months ago, I asked a bunch of stuff about super() and managed to fake it well enough to move on to other things for awhile. The day of reckoning came this week and I was forced to implement it better for my personal Python project. I have a hack in place that makes it work well-enough but I found myself frustrated with how shift the super type is. It's both the self and the parent class, but not.

If you don't know, you can trap what super() returns some time and poke it with a stick. If you print it you'll be able to tell it's definitely unique:
<super: <class 'Child'>, <Child object>>

If you try to invoke methods on it, it'll invoke the superclass' methods. That's what is supposed to happen and basically what already happens when you do super().invoke_this_thing() anyways.

Okay, so how is it doing the lookup for that? The child instance and the super types' __dict__ are the same. The contents pass an equality comparison and are the same if you print them.

They have the same __getattribute__ method wrapper. However, if you dir() them you definitely get different stuff. For one, the super type has its special variables __self__, __self_class__, and __thisclass__. It's missing __dict__ from the dir output. But wait, I just looked at that!

So I'm thinking that __getattr__ is involved, but it's not listed in anything. If I use getattr on the super, I'll get the parent methods. If I use __getattribute__, I get the child's methods. I get errors every way I've conceived of trying to pull out a __getattr__ dunder. No love.

I guess the fundamental question is: what different stuff happens when LOAD_ATTR is performed on a super object versus a regular object?

If you are curious about what I'm doing right now, I overrode __getattribute__ since that's primarily what I use for attribute lookups right now. It defer to the superclass' __getattribute__. If a method pops out, it replaces the self with the super's __self__ before kicking it out. I feel kind of dirty doing it:

https://github.com/rockobonaparte/cloaca/blob/312758b2abb80320fb3bf344ba540a034875bc4b/LanguageImplementation/DataTypes/PySuperType.cs#L36

If you want to see how I was experimenting with super, here's the code and output:

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

    def stuff(self):
        print("Parent stuff!")


class Child(Parent):
    def __init__(self):
        super().__init__()
        self.b = 2
        self.super_instance = super()

    def stuff(self):
        print("Child stuff!")

    def only_in_child(self):
        print("Only in child!")


c = Child()
c.super_instance.__init__()
c.stuff()
c.super_instance.stuff()
print(c)
print(c.super_instance)
print(c.__init__)
print(c.super_instance.__init__)
print(c.stuff)
print(c.super_instance.stuff)
print(c.__getattribute__)
print(c.super_instance.__getattribute__)
print(dir(c))
print(dir(c.super_instance))
print(c.__dict__ == c.super_instance.__dict__)
print(getattr(c, "__init__"))
print(getattr(c.super_instance, "__init__"))
print(c.__getattribute__("__init__"))
print(c.super_instance.__getattribute__("__init__"))



Child stuff!
Parent stuff!
<__main__.Child object at 0x0000026854D99828>
<super: <class 'Child'>, <Child object>>
<bound method Child.__init__ of <__main__.Child object at 0x0000026854D99828>>
<bound method Parent.__init__ of <__main__.Child object at 0x0000026854D99828>>
<bound method Child.stuff of <__main__.Child object at 0x0000026854D99828>>
<bound method Parent.stuff of <__main__.Child object at 0x0000026854D99828>>
<method-wrapper '__getattribute__' of Child object at 0x0000026854D99828>
<method-wrapper '__getattribute__' of Child object at 0x0000026854D99828>
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'b', 'only_in_child', 'stuff', 'super_instance']
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__self_class__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__thisclass__', 'a', 'b', 'super_instance']
True
<bound method Child.__init__ of <__main__.Child object at 0x0000026854D99828>>
<bound method Parent.__init__ of <__main__.Child object at 0x0000026854D99828>>
<bound method Child.__init__ of <__main__.Child object at 0x0000026854D99828>>
<bound method Child.__init__ of <__main__.Child object at 0x0000026854D99828>>


More information about the Python-list mailing list