27.Python之路 - 面向对象多态

Python之路 - 面向对象多态

介绍

上一篇中已经得知 , 继承可以扩展已存在的代码模块(类) , 其目的是为了解决代码重用 问题

多态则是为了实现另一个目的 : 接口重用

多态 🍀

多态 (Polymorphism) 按字面的意思就是”多种状态” , 比如动物有多种形态 , 人 , 猫 , 狗 ; 文件也有多种格式 exe , txt , md(MarkDown格式) , 这就是多态

在面向对象语言中 , 接口的多种不同的实现方式即为多态

多态性是允许你将父对象设置成为一个或多个他的子对象相等的技术 , 赋值之后 , 父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作

静态多态性

必须在编译时就能确定其处理方式

1
2
3
4
5
6
7
8
9
10
11
12
13
n1 = 12
n2 = 34
# int类型相加
print(n1 + n2)
s1 = 'hello '
s2 = 'word'
# str类型相加
print(s1 + s2)
'''
执行结果:
46
hello word
'''

如上述例子我们利用运算符 “+”, 完成了两种情况下的运算 , 并且Python解释器在执行前就已确定处理方式 , 即编译过程中就已经知道了调用哪个函数

动态多态性

编译时无法立即确定其处理方式 , 只有在执行时才确定处理方式 , 注意一定要同名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from abc import ABCMeta,abstractclassmethod
# 接口继承
class Animal(metaclass=ABCMeta):
@abstractclassmethod
# 约束派生类必须有talk方法
def talk(self):
pass
class Cat(Animal):
def talk(self):
print("喵喵喵")
class Dog(Animal):
def talk(self):
print("汪汪汪")
c = Cat()
d = Dog()
# 因为接口的缘故,我们无需考虑实例化后的对象具体是什么类型,因为动物都有talk方法,所以我们可以直接使用
c.talk()
d.talk()
# 我们进行接口统一
def talk(obj):
obj.talk()
talk(c)
talk(d)
'''
执行结果:
喵喵喵
汪汪汪
喵喵喵
汪汪汪
'''

上栗中, Python解释器在解释时是无法确定处理方式的 , 因为存在几个同名的方法 , 编译时并不能确定是哪一个 , 只有在执行时 , 才能确定使用哪个类中的talk() 方法 , 这就是动态多态性

小结:

  1. 静态多态性与动态多态性的区别在于 , 编译时是否能确定其处理方式
  2. 通过多态可以实现用一个函数名调用不同内容的函数

多态性的好处 🍀

多态性是面向对象的三大特性之一 , 有很多人说Python不支持多态 , 事实上Python处处是多态 , 比如内置函数len() 就是多态的一种体现

多态的作用:

  1. 增加了程序的灵活性

    以不变应万变 , 不论对象有多少中形态 , 使用者都是同一种形式去调用 , 如 talk(obj)

  2. 增加了程序的可扩展性

    通过继承Animal类派生了一个新的类 , 使用者无需更改自己的代码 , 依旧利用 talk(obj) 进行调用

对于多态 , 可能会觉得比较模糊 , 这是因为 , 我们在写程序时不知不觉就用上了 , 哈哈所以还是说处处是多态

鸭子类型 🍀

Python崇尚鸭子类型

以下是维基百科中对鸭子类型得论述 :

在程序设计中 , 鸭子类型 (英语 : duck typing) 是动态类型的一种风格。在这种风格中 , 一个对象有效的语义 , 不是由继承自特定的类或实现特定的接口 , 而是由当前方法和属性的集合决定 . 这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试 , “ 鸭子测试 “可以这样表述:

“ 如果走起来像鸭子 , 游泳起来像鸭子 , 叫起来也像鸭子 , 那么它就是鸭子 “

在鸭子类型中 , 关注的不是对象的类型本身 , 而是它是如何使用的 . 例如 , 在不使用鸭子类型的语言中 , 我们可以编写一个函数 , 它接受一个类型为鸭的对象 , 并调用它的走和叫方法 . 在使用鸭子类型的语言中 , 这样的一个函数可以接受一个任意类型的对象 , 并调用它的走和叫方法 . 如果这些需要被调用的方法不存在 , 那么将引发一个运行时错误 . 任何拥有这样的正确的走和叫方法的对象都可被函数接受的这种行为引出了以上表述 , 这种决定类型的方式因此得名。

鸭子类型通常得益于不测试方法和函数中参数的类型 , 而是依赖文档 , 清晰的代码和测试来确保正确使用 . 从静态类型语言转向动态类型语言的用户通常试图添加一些静态的 ( 在运行之前的 ) 类型检查 , 从而影响了鸭子类型的益处和可伸缩性 , 并约束了语言的动态特性

例1 : 利用标准库中定义的各种 ‘ 与文件类似的对象 ‘ , 尽管这些对象的工作方式像文件 , 但他们没有继承内置对象的方法

1
2
3
4
5
6
7
8
9
10
11
12
# 文本文件
class TxtFile:
def read(self):
pass
def write(self):
pass
# 磁盘文件
class DiskFile:
def read(self):
pass
def write(self):
pass

二者都像鸭子, 二者看起来都像文件,因而就可以当文件一样去用

例2 : 序列类型有多种形态 : 字符串 , 列表 , 元组 , 但他们没有直接的继承关系

1
2
3
4
5
6
7
8
# 三者都是序列类型
name = 'Lyon'
namel = ['Lyon']
namet = ('Lyon',)
# 字符串,列表,元组并没有直接关系,都可以调用len(),并且我们无需考虑类型
print(len(name))
print(len(namel))
print(len(namet))

本文标题:27.Python之路 - 面向对象多态

文章作者:Jesse

发布时间:2020年06月26日 - 09:06

最后更新:2020年06月30日 - 20:06

原始链接:https://jesse.top/2020/06/26/python/04-Object-Oriented/03-Python之路 - 面向对象之多态/

许可协议: 禁止商业用途 转载请保留原文链接及作者。

坚持原创技术分享,您的支持将鼓励我继续创作!