In OOP programming, when we define a class, we can inherit from an existing class, and the new class is called a subclass (subclass), and the inherited class is called the base class, the parent class, or the superclass (base-Class, Super-Class).
For example, we have written a class named Animal
, and there is a run()
way to print it directly:
class Animal(object): def run(self): print(‘Animal is running...‘)
When we need to write Dog
and Cat
class, we can inherit directly from the Animal
class:
class Dog(Animal): passclass Cat(Animal): pass
For Dog
speaking, Animal
it's the parent class, and for Animal
that, Dog
it's a subclass. Cat
and Dog
similar.
What are the benefits of inheritance? The biggest benefit is that the subclass obtains the full functionality of the parent class. Because the Animial
method is implemented, run()
Dog
and Cat
as its subclass, nothing is done, it automatically has the run()
method:
dog = Dog()dog.run()cat = Cat()cat.run()
The results of the operation are as follows:
is running...Animal is running...
Of course, you can also add some methods to the subclass, such as the Dog class:
class Dog(Animal): def run(self): print(‘Dog is running...‘) def eat(self): print(‘Eating meat...‘)
The second benefit of inheritance requires a little improvement in our code. As you can see, whether it is or not, Dog
Cat
they run()
all show Animal is running...
that the logical approach is to display Dog is running...
and Cat is running...
, therefore, the Dog
improvements to and classes are Cat
as follows:
class Dog(Animal): def run(self): print(‘Dog is running...‘)class Cat(Animal): def run(self): print(‘Cat is running...‘)
Run again and the results are as follows:
is running...Cat is running...
When both the subclass and the parent class have the same run()
method, we say that the child class run()
overrides the parent class, and the subclass is run()
always called when the code is running run()
. In this way, we gain another benefit of inheritance: polymorphism.
To understand what polymorphism is, let's start with a little more explanation of the data type. When we define a class, we actually define a data type. The data types we define are the same data types that python comes with, such as STR, list, dict:
a = list() # a是list类型b = Animal() # b是Animal类型c = Dog() # c是Dog类型
Judging whether a variable is a type can be isinstance()
judged by:
>>> isinstance(a, list)True>>> isinstance(b, Animal)True>>> isinstance(c, Dog)True
It seems a
, b
and indeed corresponds to, c
list
Animal
Dog
these 3 kinds.
But wait, try it:
>>> isinstance(c, Animal)True
It c
seems Dog
to be more than just, c
still Animal
!
But think about it, it makes sense, because it's Dog
Animal
inherited, and when we create an Dog
instance c
, we think c
the data type is Dog
right, but it's c
Animal
also true, It Dog
is Animal
a kind of!
So, in an inheritance relationship, if the data type of an instance is a subclass, its data type can also be considered a parent class. However, the reverse is not possible:
>>> b = Animal()>>> isinstance(b, Dog)False
Dog
Can be seen Animal
, but Animal
not seen Dog
.
To understand the benefits of polymorphism, we also need to write a function that takes a Animal
variable of one type:
def run_twice(animal): animal.run() animal.run()
When we pass Animal
in an instance, we run_twice()
print out:
>>> run_twice(Animal())Animal is running...Animal is running...
When we pass Dog
in an instance, we run_twice()
print out:
>>> run_twice(Dog())Dog is running...Dog is running...
When we pass Cat
in an instance, we run_twice()
print out:
>>> run_twice(Cat())Cat is running...Cat is running...
It doesn't seem to mean anything, but think about it now, if we define a Tortoise
type again, it Animal
derives from:
class Tortoise(Animal): def run(self): print(‘Tortoise is running slowly...‘)
When we call run_twice()
, the incoming Tortoise
instance:
>>> run_twice(Tortoise())Tortoise is running slowly...Tortoise is running slowly...
You will find that a new subclass is not Animal
necessary to run_twice()
make any changes, in fact, any Animal
function or method that relies on parameters can work without modification, because of polymorphism.
The advantage of polymorphism is that when we need to pass in Dog
, Cat
Tortoise
... , we just need to receive the Animal
type, because,, Dog
Cat
Tortoise
... Are all Animal
types, and then Animal
you can do so by type. Because the Animal
type has run()
methods, any type passed in, as long as it is a Animal
class or subclass, will automatically invoke the actual type of run()
method, which is the meaning of polymorphism:
For a variable, we just need to know that it is a Animal
type, without knowing exactly what its subtype is, you can safely invoke run()
the method, and the method that is called is the function of the, run()
or the Animal
Dog
Cat
Tortoise
object, Determined by the exact type of the object at run time, this is the true power of polymorphism: The caller just calls, regardless of the details, and when we add a Animal
subclass, simply make sure that the run()
method is written correctly, regardless of how the original code is called. This is the famous "opening and shutting" principle:
Open to extensions: Allow new Animal
subclasses;
Closed for modification: You do not need to modify Animal
run_twice()
such functions as dependent types.
Inheritance can also be inherited from the first level, like from Grandpa to father, and then to the son of the relationship. And any class, in the end, can be traced back to the root class object, which looks like a backward tree. For example, the following inheritance tree:
Static language vs Dynamic Language
For a static language, such as Java, if an incoming Animal
type is required, the incoming object must be a Animal
type or its subclass, otherwise the method cannot be called run()
.
For dynamic languages such as Python, the incoming type is not necessarily required Animal
. We just need to make sure that the incoming object has one run()
way to do it:
class Timer(object): def run(self): print(‘Start...‘)
This is the "duck type" of dynamic language, it does not require strict inheritance system, an object as long as "look like a duck, walk like a duck", it can be regarded as a duck.
Python's "File-like object" is a type of duck. For a real file object, it has a read()
method that returns its contents. However, many objects, as long as there are read()
methods, are considered "File-like object". Many functions receive the parameter "File-like object", you do not have to pass in the real file object, can pass any object that implements the read()
method completely.
Summary
Inheritance can take all the functions of the parent class directly, so that you do not have to re-zero, subclasses only need to add their own unique methods, but also the parent class does not fit the method overrides overrides.
The characteristics of duck type in dynamic language determine that inheritance is not necessary as a static language.
Python Inheritance and polymorphism