Search code examples
python-3.xinheritancesubclasspython-class

How __init__ works for inheritance


I cant have 2 init methods in one class because of function overloading. However, why is it possible that when initializing a subclass, im able to define a new __init__ method, and use the super().__init__ method or the parentclass init method within the subclass __init__ method. i'm just a little confused by the concept of 2 __init__ methods functioning at the same time

class Employee:
    emps = 0
    def __init__(self,name,age,pay):
        self.name = name
        self.age = age
        self.pay = pay

    
class Developer(Employee):
    def __init__(self,name,age,pay,level):
        Employee.__init__(self,name,age,pay)
        self.level = level

Solution

  • I cant have 2 init methods in one class because of function overloading.

    Partially true. You can't have 2 __init__ methods in the same class because the language lacks function overloading. (Libraries can partially restore a limited form of function overloading; see functools.singledispatchmethod for an example.)

    i'm just a little confused by the concept of 2 init methods functioning at the same time

    But you aren't trying to overload __init__. You are overriding __init__, providing a different definition for Developer than the definition it inherits from Employer. (In fact, Employer is overriding __init__ as well, using its own definition in place of the one it inherits from object.) Each class has only one definition.


    In your definition of Developer.__init__, you are simply making an explicit call to the inherited method to do the initialization common to all instances of Employee, before doing the Developer-specific initialization on the same object.


    Using super, you are using a form of dynamic lookup to let the method resolution order for instance of Developer decide what the "next" version of __init__ available to Developer to call. For single inheritance, the benefit is little more than avoiding the need to hard-code a reference to Employee. But for multiple inheritance, super is crucial to ensuring that all inherited methods (both the ones you know about and the ones you may not) get called, and more importantly, are called in the right order.

    A full discussion of how to properly use super is beyond the scope of this question, I think, but I'll show your two classes rewritten to make the best use of super, and refer you to Python's super() considered super! for more information.

    # Main rules:
    #  1. *All* classes use super().__init__, even if you are only inheriting
    #     from object, because you don't know who will use you as a base class.
    #  2. __init__ should use keyword arguments, and be prepared to accept any
    #     keyword arguments.
    #  3. All keyword arguments that don't get assigned to your own parameters
    #     are passed on to an inherited __init__() to process.
    
    class Employee:
        emps = 0
        def __init__(self, *, name, age, pay, **kwargs):
            super().__init__(**kwargs)
            self.name = name
            self.age = age
            self.pay = pay
    
        
    class Developer(Employee):
        def __init__(self, *, level, **kwargs):
            super().__init__(**kwargs)
            self.level = level
    
    
    d1 = Developer(name="Alice", age=30, pay=85000, level=1)
    

    To whet your appetite for the linked article, consider

    class A:
        def __init__(self, *, x, **kwargs):
            super().__init__(**kwargs)
            self.x = x
    
    
    class B:
        def __init__(self, *, y, **kwargs):
            super().__init__(**kwargs)
            self.y = y
    
    
    class C1(A, B):
        pass
    
    
    class C2(B, A):
        pass
    
    
    c1 = C1(x=1, y=2)
    c2 = C2(x=4, y=3)
    assert c1.x == 1 and c1.y == 2
    assert c2.x == 4 and c2.y == 3
    

    The assertions all pass, and both A.__init__ and B.__init__ are called as intended when c1 and c2 are created.