#我们前面了解了继承,继承可以帮助我们重复使用代码。但对于继承中的示例,无论是Dog还是Cat,调用父类的run()方法时显示的都是Animal is running.,如果想让结果显示为Dog is running.和Cat is running.,该如果处理呢?
#我们对Dog和Cat做如下改进:
1 #! /usr/bin/python3 2 #-*-coding:UTF-8-*- 3 #clas_多态 4 5 class Animal(object): 6 def run(self): 7 print('Animal is running.') 8 9 class Dog(Animal): 10 def run(self): 11 print('Dog is running.') 12 13 class Cat(Animal): 14 def run(self): 15 print('Cat is running.')
#执行如下语句:
1 dog=Dog() 2 print('实例化Dog类') 3 dog.run() 4 5 cat=Cat() 6 print('实例化Cat类') 7 cat.run()
#程序执行结果如下:
1 D:Pythonworkspacedatatime20171129>python class_多态.py 2 实例化Dog类 3 Dog is running. 4 实例化Cat类 5 Cat is running.
#由执行结果看到,分别得到了Dog和Cat各自的running结果。
#当子类和父类存在相同的run()方法时,子类的run()方法会覆盖父类的run()方法,在代码运行时总会调用子类的run()方法,称之为多态。
#多态来自于希腊语,意思是有多种形式。多态意味着即使不知道变量所引用的对象类型是什么,也能对对象进行操作,多态会根据对象(或类)的不同而表现出不同的行为。例如,我们在上面的Animal类中定义了run方法,Dog和Cat分别继承Animal类,并且分别定义了自己的run方法,最后Dog和Cat调用的还是自己定义的run方法。
#为了更好了解什么是多态,我们对数据类型再做一个说明。当我们定义一个类时,实际上就是定义了一种数据类型。定义的数据类型和Python自带的数据类型(如str、list、dict)没什么区别。
1 #! /usr/bin/python3 2 #-*-coding:UTF-8-*- 3 #clas_多态 4 5 class Animal(object): 6 def run(self): 7 print('Animal is running.') 8 9 class Dog(Animal): 10 pass 11 12 a=list() #a是list类型 13 b=Animal() #b是Animal类型 14 c=Dog() #c是Dog类型
#下面用isinstance()方法判断一个变量是否是某个类型。
1 print('a是否是list类型:',isinstance(a,list)) 2 print('b是否是Animal类型:',isinstance(b,Animal)) 3 print('c是否是Dog类型:',isinstance(c,Dog))
#执行结果如下:
1 D:Pythonworkspacedatatime20171129>python class_多态.py 2 a是否是list类型: True 3 b是否是Animal类型: True 4 c是否是Dog类型: True
#由执行结果看到,a,b,c确实分别为list、Animal、Dog三种类型。我们再执行如下语句:
1 print('c是否是Dog类型:',isinstance(c,Dog)) 2 print('c是否是Animal类型:',isinstance(c,Animal))
#执行结果如下:
1 c是否是Dog类型: True 2 c是否是Animal类型: True
#由执行结果看到,c既是Dog类型又是Animal类型,这怎么理解呢?
#因为Dog是从Animal继承下来的,当我们创建Dog的实例c时,我们认为c的数据类型是Dog,但c同时也是Animal,Dog本来就是Animal的一种。
#在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以看作是父类。但反过来就不行,例如如下语句:
1 print('b是否是Dog类型:',isinstance(b,Dog))
#执行结果如下:
1 b是否是Dog类型: False
#由输出结果看到,变量b是Animal的实例化对象,是Animal类型,但不是Dog类型,也就是Dog可以看成Animal,但Animal不可以看成Dog。
#我们再看一个示例。编写一个函数,这个函数接收一个Animal类型的变量,定义并执行如下函数,执行时传入Animal的实例:
1 #! /usr/bin/python3 2 #-*-coding:UTF-8-*- 3 #class_多态_2 4 5 class Animal(object): 6 def run(self): 7 print('Animal is running.') 8 9 def run_two_times(animal): 10 animal.run() 11 animal.run() 12 13 run_two_times(Animal())
#执行结果如下:
1 D:Pythonworkspacedatatime20171129>python class_多态_2.py 2 Animal is running. 3 Animal is running.
#若执行函数传入Dog的实例,操作如下:
1 #! /usr/bin/python3 2 #-*-coding:UTF-8-*- 3 #class_多态_2 4 5 class Animal(object): 6 def run(self): 7 print('Animal is running.') 8 9 class Dog(Animal): 10 def run(slef): 11 print('Dog is running.') 12 13 def run_two_times(animal): 14 animal.run() 15 animal.run() 16 17 run_two_times(Dog())
#得到的执行结果如下:
1 D:Pythonworkspacedatatime20171129>python class_多态_2.py 2 Dog is running. 3 Dog is running.
#若传入Cat的实例,操作如下:
1 #! /usr/bin/python3 2 #-*-coding:UTF-8-*- 3 #class_多态_2 4 5 class Animal(object): 6 def run(self): 7 print('Animal is running.') 8 9 class Dog(Animal): 10 def run(slef): 11 print('Dog is running.') 12 13 class Cat(Animal): 14 def run(self): 15 print('Cat is running.') 16 17 def run_two_times(animal): 18 animal.run() 19 animal.run() 20 21 run_two_times(Animal()) 22 run_two_times(Dog()) 23 run_two_times(Cat())
#得到执行结果如下:
1 D:Pythonworkspacedatatime20171129>python class_多态_2.py 2 Animal is running. 3 Animal is running. 4 Dog is running. 5 Dog is running. 6 Cat is running. 7 Cat is running.
#看上去没有什么特殊的地方,已经正确输出预期结果了,但仔细想想看,如果再定义一个Bird类型,也继承Animal类,定义如下:
1 #! /usr/bin/python3 2 #-*-coding:UTF-8-*- 3 #class_多态_2 4 5 class Animal(object): 6 def run(self): 7 print('Animal is running.') 8 9 class Dog(Animal): 10 def run(slef): 11 print('Dog is running.') 12 13 class Cat(Animal): 14 def run(self): 15 print('Cat is running.') 16 17 class Bird(Animal): 18 def run(self): 19 print('Bird if flying the sky.') 20 21 def run_two_times(animal): 22 animal.run() 23 animal.run() 24 25 run_two_times(Animal()) 26 run_two_times(Dog()) 27 run_two_times(Cat()) 28 run_two_times(Bird())
#程序执行结果如下:
1 D:Pythonworkspacedatatime20171129>python class_多态_2.py 2 Animal is running. 3 Animal is running. 4 Dog is running. 5 Dog is running. 6 Cat is running. 7 Cat is running. 8 Bird if flying the sky. 9 Bird if flying the sky.
#由执行结果看到,新增的Animal子类不必对run_two_times()方法做任何修改。实际上,任何依赖Animal作为参数的函数或方法都可以不加修改地正常运行,原因就在于多态。
#多态的好处是:当我们需要传入Dog、Cat、Bird等对象时,只需要接收Animal类型就可以了,因为Dog、Cat、Bird等都是Animal类型,按照Animal类型进行操作即可。由于Animal类型有run()方法,因此传入的类型只要是Animal类或继承自Animal类,都会自动调用实际类型的run()方法。
#多态的意思是:对于一个变量,我们只需要知道它是Animal类型,无需确切知道它的子类型,就可以放心调用run()方法。具体调用的run()方法作用于Anial、Dog、Cat或Bird对象,由运行该对象的确切类型决定。
#多态真正的威力在于:调用方只管调用,不管细节。当我们新增一种Animal的子类时,只要确保run()方法编写正确即可,不用管原来的代码如何调用的,这就是著名的“开闭”原则:对于扩展开放,允许新增Animal子类;对于修改封闭,不需要修改依赖Animal类型的run_two_times()等函数。
#很多函数和运算符都是多态的,也许你写的程序也可能时,即使你并非有意这样的。只要使用多态函数和运算符,多态就会消除。唯一能够毁掉多态的是使用函数显式地检查类型,如type、isinstance函数等,如果有可能,就尽量避免使用这些会毁掉多态的方式,重要的是如何让对象按照我们希望的方式工作,无论它是否是正确类型或类。