08月24, 2020

Should subclass always invoke superclass' init function?

Python is an Object Oriented programming language. Inheriting is such a common case, and mostly we will invoke the superclass' init function just in the subclass' init function.

#!/usr/bin/python
class Base:
    def __init__(self, name = "Lucy"):
        self.name = name

    def say(self):
        print(f"{self.__class__} said: Hello!")

class Child(Base):
    def __init__(self, color):
        super().__init__()
        self.color = color

But is the super().init() a must? What if we Comment out the second last line.

class Child(Base):
    def __init__(self, color):
        # super().__init__()
        self.color = color

and then run a test:

# new superclass
base = Base("Lisa")
print(base.name)
base.say()

# new subclass
child = Child("blue")
try:
        # try to get superclass' attribute: name
    print(child.name)
    print(child.color)
except Exception as e:
    print(e)

# invoke an inheited method: say
child.say()

We got output like this:

Lisa
<class '__main__.Base'> said: Hello!
'Child' object has no attribute 'name'
<class '__main__.Child'> said: Hello!

The subclass still inherited all methods of the superclass, but the attribute 'name' has gone.

Why?

Python is different from other OOP languages such as C++ or Java, etc. The subclass constructure does not depend on its' superclass. Let's make some changes to see what happend in the init function.

#!/usr/bin/python
class Base:
    def __init__(self, name = "Lucy"):
        print(f"befor __init__, {self.__class__}:")
        print(dir(self))
        self.name = name
        print(f"after __init__, {self.__class__}:")
        print(dir(self))

    def say(self):
        print(f"{self.__class__} said: Hello!")


class Child(Base):
    def __init__(self, color):
        print(f"befor __init__, {self.__class__}:")
        print(dir(self))
        # super().__init__()
        self.color = color
        print(f"after __init__, {self.__class__}:")
        print(dir(self))

Then run the same test code, we got a long output, this is a part of it:

befor __init__, <class '__main__.Child'>:
['__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__', 'say']
after __init__, <class '__main__.Child'>:
['__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__', 'color', 'say']

As we can see, before the init function invoked, sublass has already inherited the method "say" and the attribute "name" is added to the subclass right during the init function.

That means if the logic in superclass‘ init function is not necessary it can be just ignored.

本文链接:http://www.thinkinpython.com/post/EN_1.html

-- EOF --