目录
一. 多态的概念简介
二. 多态的例子一:偏好过滤器
三. 多态的例子二:审查描述符(需要元编程知识)
四. 总结
面向对象编程(Object Oriented Programming,OOP)有三大核心概念:封装,继承,多态。前两个概念广为人知,却有很多人不清楚“多态”这个概念。下面我们就来简单谈谈多态究竟是怎么一回事。
一. 多态的概念简介
所谓多态,简单地说就是指为不同数据类型的实体提供统一的接口,而不同的类型其调用效果不同。
例如,下面的Python“动物”抽象类定义了“叫”抽象方法,这要求所有继承自动物的类必须各自实现自己的“叫”方法。依靠这种特性,当我们知道一个类型是“动物”的具体子类时,就知道它一定支持“叫”这个接口。但是,“叫”调用的结果随着具体类型的不同而不同:
from abc import ABC, abstractmethodclass 动物(ABC):@abstractmethoddef 叫(self):'''动物应当会发声'''class 狗子(动物):def 叫(self):print('汪汪汪!')
class 猫咪(动物):def 叫(self):print('喵喵喵~')x = 狗子()
y = 猫咪()lst = [1, 2, x, 3 ,y]for obj in lst:if isinstance(obj, 动物):obj.叫()
在这段代码中,我们遍历许多对象,如果对方通过了针对“动物”的实例检查,我们就知道它一定有“叫”这个方法,从而放心地进行调用。行为的不同就是多态的体现:

说白了,其实多态不就是这点事吗(* ̄▽ ̄)~*,严谨的知识我会在最后给出。下面我们来看两个具体使用多态的例子,通过例子理解起来是最有效的。
二. 多态的例子一:偏好过滤器
比方说我在网上爬来了下面这样有关作品及其章节的数据(假设这是个很大的数据):
datas = [{'标题': '母猪的产后护理','来源': '《念诗之王》'},{'标题': '物理人的自我修养','来源': '《大雪牲思想道德修养》'},{'标题': '全拿来糊墙了','来源': '《念诗之王》'},{'标题': '他们朝我扔粑粑','来源': '《四川芬达》'}
]
而我对《念诗之王》这部作品情有独钟,只想从数据中提取有关它的章节。那么我可以写出下面这段代码,判断来源是不是《念诗之王》,从而决定要不要收集它:
class 数据筛选器:def __init__(self, 数据):self.数据 = 数据def 筛选数据(self):结果 = []for 项目 in self.数据:if 项目['来源'] == '《念诗之王》':结果.append(项目['标题'])return 结果我的筛选器 = 数据筛选器(datas)print(我的筛选器.筛选数据())

但是,人的激情毕竟是一阵一阵的。假设我突然改为喜欢《大雪牲思想道德修养》 这部作品了,只想收集有关它的标题,那就必须硬编码修改程序:
class 数据筛选器:def __init__(self, 数据):self.数据 = 数据def 筛选数据(self):结果 = []for 项目 in self.数据:if 项目['来源'] == '《大雪牲思想道德修养》':结果.append(项目['标题'])return 结果
每次需求改变,就必须硬性修改程序代码,这未免也太不好用了吧?
但如果我能把“筛选”的逻辑独立出来,给这个“数据筛选器”类新增一个“过滤器”属性,把检查逻辑委托给“过滤器”,那么就可以依赖过滤器的多态来适应各种情况。先顺着这个思路改写一下“数据筛选器”类:
class 数据筛选器:def __init__(self, 数据, 过滤器):self.数据 = 数据self.过滤器 = 过滤器def 筛选数据(self):结果 = []for 项目 in self.数据:if self.过滤器.要保留(项目):结果.append(项目['标题'])return 结果
这里,动态的筛选过程依赖过滤器“要保留”方法的多态。
然后,我们来具体实现过滤器,下面是完整代码清单:
from abc import ABC, abstractmethoddatas = [{'标题': '母猪的产后护理','来源': '《念诗之王》'},{'标题': '物理人的自我修养','来源': '《大雪牲思想道德修养》'},{'标题': '全拿来糊墙了','来源': '《念诗之王》'},{'标题': '他们朝我扔粑粑','来源': '《四川芬达》'}
]
# "基过滤器"是抽象基类,它的"要保留"方法依赖
# 它的抽象方法"获取保留值"
class 基过滤器(ABC):
# 这个方法定义为抽象方法,强制要求子类必须实现@abstractmethoddef 获取保留值(self): '''子类必须声明具体保留值'''def 要保留(self, 项目): if 项目['来源'] in self.获取保留值():return Truereturn False
# 这个子类过滤器具体实现了抽象方法
class 念诗之王过滤器(基过滤器): def 获取保留值(self):return ['《念诗之王》']class 数据筛选器:def __init__(self, 数据, 过滤器):self.数据 = 数据self.过滤器 = 过滤器def 筛选数据(self):结果 = []for 项目 in self.数据:if self.过滤器.要保留(项目):结果.append(项目['标题'])return 结果# 传入子类过滤器的实例,利用它的多态性完成筛选
念诗之王筛选器 = 数据筛选器(datas, 念诗之王过滤器()) print(念诗之王筛选器.筛选数据())

像这样依赖对象的多态性实现的方案具有很强的可扩展性。例如,假设我突然想要获取《念诗之王》和《四川芬达》这两部作品的标题,只要像下面这样:
class 四川之王过滤器(基过滤器):def 获取保留值(self):return ['《念诗之王》', '《四川芬达》']四川之王筛选器 = 数据筛选器(datas, 四川之王过滤器())print(四川之王筛选器.筛选数据())

这是第一个例子。下面我们再看一个例子, 依赖子类型的多态来实现拒绝非法数据。
三. 多态的例子二:审查描述符(需要元编程知识)
在Python中,使用“三思而后行”策略编程是容易碰壁的,因为Python超强的动态特性使得全面排查非法输入繁琐而困难。要是这些审查逻辑还不能复用,那就太折磨人了!
幸好,属性描述符这个高级特性非常适合用来审查输入。下面我们直接看基类的逻辑:
from abc import ABC, abstractmethod
# 这是一个抽象基类描述符
class 审判者(ABC):
# 前两个方法都是标准实现def __set_name__(self, owner, name):self.name = namedef __get__(self, instance, name):return instance.__dict__[self.name]
# 它的设置值逻辑依赖这个抽象方法,子类必须实现。@abstractmethoddef 审判(self, value):'''审查值,如果不通过就抛出异常。'''
# 如果审判不通过就报错,让设置值无法成功。
# 如果不报错,那就能顺利设置值。def __set__(self, instance, value):self.审判(value)instance.__dict__[self.name] = value
假设我正在编写一个Person类,它需要身高和体重两个参数,这两个参数都必须是正数。那我就可以定义一个“正数审查员”具体子类:
class 正数审查员(审判者):def 审判(self, value):if not isinstance(value, (float, int)) or isinstance(value, bool):raise TypeError(f'{self.name}属性必须是正数,不能是{value.__class__.__name__}类型')if value <= 0:raise ValueError(f'{self.name}属性必须是正数,不能是{value!r}')
然后,在Person类中使用它,就可以利用“审判”的多态来拒绝非正数输入:
class Person:height = 正数审查员()weight = 正数审查员()def __init__(self, height, weight):self.height = heightself.weight = weight
传入合法参数,实例成功创建:
Person(170, 50)
传入非法参数,拒绝创建实例:
Person(-170, -50)
以及:
Person(True, -50)
完整代码清单:
from abc import ABC, abstractmethodclass 审判者(ABC):def __set_name__(self, owner, name):self.name = namedef __get__(self, instance, name):return instance.__dict__[self.name]@abstractmethoddef 审判(self, value):'''审查值,如果不通过就抛出异常。'''def __set__(self, instance, value):self.审判(value)instance.__dict__[self.name] = value
class 正数审查员(审判者):def 审判(self, value):if not isinstance(value, (float, int)) or isinstance(value, bool):raise TypeError(f'{self.name}属性必须是正数,不能是{value.__class__.__name__}类型')if value <= 0:raise ValueError(f'{self.name}属性必须是正数,不能是{value!r}')class Person:height = 正数审查员()weight = 正数审查员()def __init__(self, height, weight):self.height = heightself.weight = weightPerson(170, 50)
四. 总结
我本来想写更多更细的,但是突然感觉写不动了QAQ。下面来总结一下多态的知识吧。
多态(Polymorphism)是面向对象编程(OOP)的核心特性之一,其核心思想是“同一接口,多种实现”。通过多态,不同的对象对同一操作可以表现出不同的行为,从而提升代码的灵活性和可扩展性。
多态的优点:
- 灵活性:
- 同一接口适用于不同对象,无需为每种类型编写独立代码。
- 例如:统一调用 animal.sound(),无论 animal 是 Dog、Cat 还是其他动物。
- 可扩展性:
- 新增子类时无需修改现有代码,只需遵循接口规范。
- 例如:新增 Bird 类并实现 sound(),无需改动调用方。
- 代码复用:
- 通过继承和接口抽象,减少重复代码,提高开发效率。
实现条件:
- 继承关系:多态通常基于类的继承或接口的实现。
- 方法重写:子类必须重写父类的方法(或实现接口方法)。
- 引用传递:父类引用(或接口引用)指向子类对象。





















