首先要明白几个概念:
bart = Student(‘Bart Simpson‘, 59); bart的数据类型就是“Student类”;
理解:其实Student这个类,就是一个模板,一种算法,而我们创建的实例对象bart,是bart变量指向的存储单元存储了:
1.指向Student这个类的地址;
2..(‘Bart Simpson‘, 59)这组信息。
再次理解:bart.方法 = Student(‘Bart Simpson‘, 59).方法;
a = list() # a是list类型
b = Animal() # b是Animal类型 可见,每一个具体的class就是一个具体的类型
c = Dog() # c是Dog类型 且Dog是Animal的子类
判断一个变量是否是某个类型可以用isinstance()
判断:
>>> isinstance(a, list)
True
>>> isinstance(b, Animal)
True
>>> isinstance(c, Dog)
True
在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。但是,反过来就不行:
>>> b = Animal()
>>> isinstance(b, Dog)
False
多态的好处就是,当我们需要传入Dog
、Cat
、Tortoise
……时,我们只需要接收Animal
类型就可以了,因为Dog
、Cat
、Tortoise
……都是Animal
类型,然后,按照Animal
类型进行操作即可。由于Animal
类型有run()
方法,因此,传入的任意类型,只要是Animal
类或者子类,就会自动调用实际类型的run()
方法,这就是多态的意思:
对于一个变量,我们只需要知道它是Animal
类型,无需确切地知道它的子类型,就可以放心地调用run()
方法,而具体调用的run()
方法是作用在Animal
、Dog
、Cat
还是Tortoise
对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Animal
的子类时,只要确保run()
方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:
对扩展开放:允许新增Animal
子类;
对修改封闭:不需要修改依赖Animal
类型的run_twice()
等函数。
继承还可以一级一级地继承下来,就好比从爷爷到爸爸、再到儿子这样的关系。而任何类,最终都可以追溯到根类object,这些继承关系看上去就像一颗倒着的树。比如如下的继承树:
对于静态语言(例如Java)来说,如果需要传入Animal
类型,则传入的对象必须是Animal
类型或者它的子类(他的子类依然可以看作是Animal类),否则,将无法调用run()
方法,因为Java定义函数需要提前声明数据或变量类型,数据类型对不上函数当然无法执行,这就是静态语言的特性。
对于Python这样的动态语言来说,则不一定需要传入Animal
类型(可以是Sun类,可以是Sea类等等)(因为python不需要你声明参数或数据类型,他的适应性很强)。我们只需要保证传入的对象有一个run()
方法就可以了:
class Timer(object):
def run(self):
print(‘Start...‘)
这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子(症结所在:python不限定传入的数据类型)。
补充关于鸭子类型的概念:
首先要明确python里“鸭子类型”的概念:
1、看起来像鸭子(举例:一个纸做的手工,参考了鸭子的外形)
2、走起路来像鸭子(这个手工作品,实现了八字走路,而且内置了扬声器可以嘎嘎叫)
总结:那么我们可以认为这个纸工艺品,它就是鸭子。
这就是python的灵活之处,不拘泥于静态类型的严格定义,只要这个类型内部有run()函数,可以看作是目标类型。(再做个补充,其实不管是timer类也好,还是其他什么类也好,只要有run()函数,都可以传入到run_twice里运行,因为他们满足run_twice里需要运行run()函数的需要)
郑重声明:以上内容是基于 廖雪峰的python教程 整理的,不是原创,不喜勿喷。
原文:https://www.cnblogs.com/Jie-Bian/p/11053203.html