Python learning-inheritance and polymorphism

In OOP programming, when we define a class, we can inherit from an existing class. The new class is called a subclass , and the inherited class is called a base class, a parent class or a super class. (Base class, Super class) .
For example, we have written a class called Animal, and there is a run() method that can print directly:

class Animal(object):
	def run(self):
		print('Animal is running...')

When we need to write the Dog and Cat classes, we can directly inherit from the Animal class:
Inheritance is to change the object in brackets to the class to be inherited :

class Dog(Animal):
	pass
class Cat(Animal):
	pass

For Dog, Animal is its parent class, and for Animal, Dog is its subclass. Cat and Dog are similar.
What are the benefits of inheritance? The biggest advantage is that the child class has all the functions of the parent class. Because Animal implements the run() method, Dog and Cat, as its subclasses, do not do anything, they automatically have the run() method

dog = Dog()
dog.run()

cat = Cat()
cat.run()

The results are as follows:

Animal is running...
Animal is running...

Of course, you can also add some methods to subclasses, 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 us to make a little improvement to the code. As you can see, whether it is Dog or Cat, when they run(), they display Animal is running.... The logical approach is to display Dog is running... and Cat is running... separately. Therefore, for Dog and Cat The improvements are as follows:

class Dog(Animal):
	
	def run(self):
		print('Dog is running...')

class Cat(Animal):
	
	def run(self):
		print('Cat is running...')

Run again, the results are as follows:

Dog is running...
Cat is running...

Note here :
When both the subclass and the parent class have the same run() method, we say that the run() of the subclass overrides the run() of the parent class. When the code is running, the subclass will always be called run() . In this way, we have another benefit of inheritance: polymorphism.

To understand what polymorphism is, we must first explain the data types. When we define a class, we actually define a data type. The data types we define are no different from the data types that Python comes with, such as str, list, and dict

a = list() # a是list类型
b = Animal() # b是Animal类型
C = Dog() # c是Dog类型

To determine whether a variable is of a certain type, you can use isinstance() to determine:

>>> isinstance(a, list)
True
>>> isinstance(b, Animal)
True
>>> isinstance(c, Dog)
True

It seems that a, b, and c indeed correspond to the three types of list, animal, and dog.
But wait, try:

>>> isinstance(c, Animal)
True

It seems that c is not just Dog, c is Animal!
But think about it carefully, it makes sense, because Dog is inherited from Animal. When we create an instance of Dog C, we think that the data type of C is Dog, but C is also Animal. Dog was originally a kind of Animal.
Therefore, in the inheritance relationship, if the data type of an instance is a subclass, its data type can also be regarded as the parent class. However, the reverse will not work :

>>> b = Animal()
>>> isinstance(b, Dog)
False

Dog can be regarded as Animal, but Animal cannot be regarded as Dog.
To understand the benefits of polymorphism, we also need to write another function that accepts an Animal type variable:
there is a problem here, because Python is a dynamic language, you can pass any type of parameters to it, just for the time being This way of understanding, in order to understand polymorphism :

def run_twice(animal):
    animal.run()
    animal.run()

When we pass in an instance of Animal, run_twice() will print out:

>> run_twice(Animal())
Animal is running...
Animal is running...

When we pass in an instance of Dog, run_twice() will print out:

>>> run_twice(Dog())
Dog is running...
Dog is running...

When we pass in an instance of Cat, run_twice() will print out:

>>> run_twice(Cat())
Cat is running...
Cat is running...

It doesn't look interesting, but think about it, now, if we define another Tortoise type, it also derives from Animal:

class Tortoise(Animal):
	def run(self):
		print('Tortoise is running slowly...')

When we call run_twice(), we pass in an instance of Tortoise:

>>> run_twice(Tortoise())
Tortoise is running slowly...
Tortoise is running slowly...

You will find that adding a subclass of Animal does not require any modification to run_twice(). In fact, any function or method that relies on Animal as a parameter can run normally without modification. The reason is polymorphism.

The advantage of polymorphism is that when we need to pass in Dog, Cat, Tortoise..., we only need to receive the Animal type, because Dog, Cat, Tortoise... are all Animal types, and then operate according to the Animal type That's it. Since the Animal type has a run() method, any type passed in, as long as it is an Animal class or a subclass, will automatically call the actual type's run() method. This is what polymorphism means:
this passage is ambiguous. This does not require that the input must be of the Animal type, but any type of parameter, only the run() method is required, but it is only used to understand polymorphism.

For a variable, we only need to know that it is of the Animal type, without knowing exactly its subtype, we can safely call the run() method, and the specific run() method called works on Animal, Dog, Cat or Tortoise On the object, it is determined by the exact type of the object at runtime. This is the true power of polymorphism: the caller just calls, regardless of the details, and when we add a subclass of Animal, ** just ensure the run() method Write it correctly, regardless of how the original code is called. This is the famous "open and close" principle:

Open to extension: Allow new Animal subclasses;

Close to modification: no need to modify functions such as run_twice() that depend on the Animal type.
Inheritance can also be inherited level by level, just like the relationship from grandfather to father to son. And any class can be traced back to the root class object, and these inheritance relationships look like an inverted tree. For example, the following inheritance tree:

				┌───────────────┐
                │    object     │
                └───────────────┘
                        │
           ┌────────────┴────────────┐
           │                         │
           ▼                         ▼
    ┌─────────────┐           ┌─────────────┐
    │   Animal    │           │    Plant    │
    └─────────────┘           └─────────────┘
           │                         │
     ┌─────┴──────┐            ┌─────┴──────┐
     │            │            │            │
     ▼            ▼            ▼            ▼
┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐
│   Dog   │  │   Cat   │  │  Tree   │  │ Flower  │
└─────────┘  └─────────┘  └─────────┘  └─────────┘

Static language vs dynamic language

For static languages ​​(such as Java), if you need to pass in the Animal type, the object passed in must be the Animal type or its subclass, otherwise, the run() method cannot be called.

For dynamic languages ​​like Python, it is not necessary to pass in the Animal type. We only need to ensure that the passed object has a run() method :

class Timer(object):
    def run(self):
        print('Start...')

This is interesting, the interesting part of dynamic language.
This is the "duck type" of dynamic languages. It does not require a strict inheritance system. As long as an object " looks like a duck and walks like a duck ", then it can be regarded as a duck .
Python's "file-like object" is a duck type. For real file objects, it has a read() method that returns its content. However, many objects, as long as they have a read() method, are regarded as "file-like objects". The parameters received by many functions are "file-like objects". You don't have to pass in a real file object, you can pass in any object that implements the read() method.

summary

Inheritance can take all the functions of the parent class directly, so that there is no need to restart from zero. The subclass only needs to add its own unique methods, and can also override the methods that are not suitable for the parent class.

The duck type characteristic of dynamic languages ​​determines that inheritance is not necessary like static languages.

Guess you like

Origin blog.csdn.net/qq_44787943/article/details/112504819