1

I understand how to initialize a parent class to get their instance attributes in a child class, but not exactly what's going on behind the scenes to accomplish this. (Note: not using super intentionally here, just to make illustration clear)

Below we extend class A by adding an extra attribute y to the child class B. If you look at the class dict after instantiating b=B(), we rightfully see both b.x(inherited from class A) and b.y.

I assume at a high level this is accomplished by the call to A.__init__(self,x=10) performing something similar to b.x=10 (the way a normal instance attribute would be assigned) within the __init__ of class B. It's a bit unclear to me because you are calling the __init__ of class A, not class B, yet class B still gets it's instance attributes updated accordingly. How does class A's __init__ know to update b's instance attributes.

This is different than inherited methods where the b object has no explicit inherited method in it's particular namespace, but looks up the inheritance chain when a call to a missing method is made. With the attribute, the method is actually in b's namespace (it's instance dict).

  class A:
        def __init__(self,x):
            self.x = x

    class B(A):
        def __init__(self):
            A.__init__(self,x=10)
            self.y = 1 

    b = B() 
    print(b.__dict__) 
    >>>{x:10,y:1} #x added to instance dict from parent init

Below we inherit from the built-in list. Here, similar to the above, since we are calling the list's __init__ method within Foolist's __init__, I would expect to see an instance dictionary that contains elems, but it is nowhere to be found. The values 123 are in the object somewhere, as can be seen by printing alist, but not in the instance dict.

class Foolist(list):
    def __init__(self, elems):
        list.__init__(self, elems)

alist = Foolist('123')

So what exactly is going on in the inheriting class when a parent's __init__ is called from a child's __init__? How are values being bound? It seems different from method lookup, as you are not searching the inheritance chain on demand, but actually assigning values to the inheriting class's instance dict.

How does a call to a parents init fill out it's child's instance dict? Why does the Foolist example not do this?

1 Answer 1

2

The answer is simple: self.

As a very rough overview, when instantiating a class, an object is created. This is more or less literally just an empty container without affiliation to anything.* This "empty container" is then passed to the __init__ method of the class that is being instantiated, where it becomes... the self argument! You're then setting an attribute on that object. You're then calling a different class's __init__ method, explicitly passing your specific self object to that method; that method then adds another attribute to the object.

This is in fact how every instance method works. Each method implicitly receives the "current object" as its first argument, self. When calling a parent's __init__ method, you're actually making that object passing very explicit.

You can approximate that behaviour with this simple example:

def init_a(obj):
    obj.x = 10

def init_b(obj):
    init_a(obj)
    obj.y = 20

o = {}
init_b(o)

* The object is not entirely "empty", there are particular attributes set on the object which create an affiliation with a particular class, so the object is "an instance of" a certain class, and Python can locate all the methods it so inherits from the class as needed.

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

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.