来源:《Python从小白到大牛》关东升
多态性
在面向对象程序设计时,多态是一个非常重要的特性,理解多态有利于进行面向对象的分析与设计。
1.多态概念
发生多态要有两个前提条件:第一、继承——多态发生一定是子类和父类之间;第二、重写——子类重写了父类的方法。父类Figure(几何图形)有一个draw(绘图)函数,Figure(几何图形)函数有两个子类Ellipse(椭圆形)和Triangle(三角形),Ellipse和Triangle重写draw()方法。Ellipse和Triangle都有draw()方法,但具体实现的方式不同
# 几何图形
class Figure:
def draw(self):
print('绘制Figure...')
# 椭圆形
class Ellipse(Figure):
def draw(self):
print('绘制Ellipse...')
# 三角形
class Triangle(Figure):
def draw(self):
print('绘制Triangle...')
f1=Figure()
f1.draw # 绘制Figure... 不符合,没有发生多态
f2=Ellipse()
f2.draw() # 绘制Ellipse... 符合多态的两个前提
f3=Triangle()
f3.draw() # 绘制Triangle... 符合多态的两个前提
多态发生时,Python解释器根据引用指向的实例调用它的方法。
提示:与Java等静态语言相比,多态性对于动态语言Python而言意义不大。多态性优势在于运行期动态特征。
例如在Java中多态是指,编译期声明变量是父类的类型,在运行期确定变量所引用的实例。而Python不需要声明变量的类型,没有编译,直接由解释器运行,运行期确定变量所引用的实例。
2.类型检查
无论多态性对Python影响多大,Python作为面向对象的语言多态性是存在的,这一点可以通过运行期类型检查证实,运行期类型检查使用isinstance(object,classinfo)函数,它可以检查object实例是否由classinfo类或classinfo子类所创建的实例。
# 几何图形
class Figure:
def draw(self):
print('绘制Figure...')
# 椭圆形
class Ellipse(Figure):
def draw(self):
print('绘制Ellipse...')
# 三角形
class Triangle(Figure):
def draw(self):
print('绘制Triangle...')
f1=Figure()
f1.draw # 绘制Figure... 不符合,没有发生多态
f2=Ellipse()
f2.draw() # 绘制Ellipse... 符合多态的两个前提
f3=Triangle()
f3.draw() # 绘制Triangle... 符合多态的两个前提
print(isinstance(f1,Triangle)) # False
print(isinstance(f2,Triangle)) # False
print(isinstance(f3,Triangle)) # True
print(isinstance(f2,Figure)) # True
isinstance(f2,Figure)表达式是True,f2是Ellipse类创建的实例,Ellipse是Figure类的子类,所以这个表达式返回True,通过这样的表达式可以判断是否发生了多态。另外,还有一个类似于isinstance(object,classinfo)的
issubclass(class,classinfo)函数,issubclass(class,classinfo)函数用来检查class是否是classinfo的子类。
print(issubclass(Ellipse,Triangle)) # False
print(issubclass(Ellipse,Figure)) # True
print(issubclass(Triangle,Ellipse)) # False
3.鸭子类型
多态性对于动态语言意义不是很大,在动态语言中有一种类似检查称为“鸭子类型”即一只鸟走起来像鸭子、游起来像鸭子、叫起来也像鸭子,那它就可以被当作鸭子。鸭子类型不关注变量的类型,而是关注变量具有的方法。鸭子类型像多态一样工作,但是没有继承,只要像“鸭子”一样的行为(方法)就可以了。
鸭子类型示例代码如下:
class Animal(object):
def run(self):
print('动物跑...')
class Dog(Animal):
def run(self):
print('狗狗跑...')
class Car:
def run(self):
print('汽车跑...')
def go(Animal): # 接收参数是Animal
animal.run()
go(Animal) # 动物跑...
go(Dog) # 狗狗跑...
go(Car) # 汽车跑...
上述代码定义了三个类Animal、Dog和Car,从代码可以看出Dog继承了Animal,而Car与Animal和Dog没有任何的关系,只是他们都有run()方法。上述代码定义了go()函数,设计时考虑接受Animal类型参数,但是由于Python解释器不做任何的类型检查,所以可以传入任何的实际参数。当在go()函数传入Car实例时,它可以正常执行,这就是鸭子类型。
在Python这样的动态语言中使用“鸭子类型”替代多态性设计,能够充分地发挥Python动态语言特点,但是也给软件设计者带来了困难,对程序员的要求也非常高。