☰
python基础之多态
2026/10/13 13:05:31 网站建设 项目流程

前言


多态(polymorphism)的字面意思是"多种形态":同一个调用、同一个方法名,作用在不同类型的对象上,会各自产生对应的行为。它解决的核心问题是把调用方和实现方解耦——调用方只说"给我算个面积",不必知道手里拿的到底是圆、矩形还是三角形。


关于多态,有两个特别常见的误解:


第一,以为在 Python 里必须先有继承、甚至必须先有抽象基类,才能实现多态。实际情况是,Python 的多态主要靠鸭子类型(duck typing):只要对象有同名的方法,调用就能成功,跟它继承自谁没有必然关系。继承只是组织代码的众多手段之一,不是多态的前提。


第二,以为"多态"就是写一长串if isinstance(x, A): ... elif isinstance(x, B): ...。这恰恰是反模式:真正的多态是把差异下沉到各个类自己的方法实现里,调用方只写一次x.area()。


本文先讲多态在 Python 里的落地方式,再讲它和isinstance的关系,最后给一个可以直接复制运行的例子。


一、多态的本质:调用方只认方法名


多态描述的是一个调用点:obj.area()。这套机制成立只需要一个条件——obj所属的类里能找到area这个方法。至于这个方法是怎么实现的、这个类继承自哪里、有没有实现某个接口,调用点一概不关心。


# 适用于 Python 3.8+
class Circle:
def __init__(self, r):
self.r = r

def area(self):
return 3.14159 * self.r ** 2


class Rectangle:
def __init__(self, w, h):
self.w = w
self.h = h

def area(self):
return self.w * self.h


def total_area(shapes):
return sum(s.area() for s in shapes)


shapes = [Circle(1), Rectangle(2, 3)]
print(total_area(shapes))

Circle和Rectangle之间没有任何继承关系,total_area照样能工作。这就是鸭子类型:它管的是"你能不能响应area这个请求",而不是"你是谁的孩子"。Python 官方词汇表对这种风格的描述是——不看对象的类型,而是直接调用方法或使用属性,用接口而不是具体类型来获得灵活性。


二、继承式多态:把公共接口写进基类


鸭子类型灵活,但缺少约束。当一组类确实共享一整套接口时,用继承把公共方法名固定下来,可读性和可维护性更好,也更方便静态类型检查器工作。


# 适用于 Python 3.8+
class Shape:
def area(self):
raise NotImplementedError("子类必须实现 area()")


class Circle(Shape):
def __init__(self, r):
self.r = r

def area(self):
return 3.14159 * self.r ** 2


class Rectangle(Shape):
def __init__(self, w, h):
self.w = w
self.h = h

def area(self):
return self.w * self.h

基类Shape.area故意只抛NotImplementedError,这是"声明接口"的常见写法。但要注意,这种约束是软约束:它只在运行时调用到才会报错,而且如果某个子类压根不定义area,只要不去调它,程序也不会崩。想要编译期(准确说是导入/实例化时)就拦住,需要抽象基类abc.ABC加@abstractmethod,那是更强的一种约束。


三、多态与 isinstance:什么时候真需要类型检查


多态和isinstance并不冲突,它们是两个层面的工具。多态用于分发行为;isinstance用于做决策,比如选择要不要把对象交给某个函数。


isinstance(x, C)会检查x是不是C的实例,包括直接、间接和虚拟子类(通过register()注册的类)。它的第二个参数还可以是类型元组,只要是其中之一就返回True。


# 适用于 Python 3.8+
def describe(value):
if isinstance(value, (int, float)):
return f"数值:{value}"
if isinstance(value, str):
return f"文本长度:{len(value)}"
return "未知类型"

这里用isinstance是合理的,因为函数确实要按类型分支。但不要用type(value) == int替代它:type()不看继承链,子类实例会被判为不相等,也会漏掉虚拟子类。


四、运算符多态与函数多态


多态不限于普通方法。Python 的运算符也是靠特殊方法(special method,也就是常说的 dunder 方法)实现多态的:+会去调__add__,len()会去调__len__,for会去调__iter__。


# 适用于 Python 3.8+
class Money:
def __init__(self, amount):
self.amount = amount

def __add__(self, other):
if not isinstance(other, Money):
return NotImplemented
return Money(self.amount + other.amount)

def __repr__(self):
return f"Money({self.amount})"


print(Money(3) + Money(4)) # Money(7)

注意__add__里返回NotImplemented(不是NotImplementedError):这是告诉解释器"我不认识这个类型,你去试试对方对象的__radd__",是运算符多态的协作约定。


常见坑点


1. 用type()判等代替isinstance()


❌if type(obj) == Shape:—— 子类的实例会被判为False。


✅if isinstance(obj, Shape):—— 会沿着继承链正确识别子类与虚拟子类。


2. 以为没有继承就没有多态


❌ 认为"必须写class Circle(Shape)才能让total_area工作"。


✅ 只要对象有area()方法即可,鸭子类型不要求继承关系。


3. 用一长串isinstance分支代替多态


❌if isinstance(s, Circle): ... elif isinstance(s, Rectangle): ...—— 每加一种形状都要改这个函数。


✅ 让每个类自己实现area(),调用方只写s.area()。


4. 基类抛NotImplementedError却指望一实例化就报错


❌Shape()能正常创建,直到调area()才炸。


✅ 需要更早拦截时改用abc.ABC+@abstractmethod,这类类根本不允许实例化。


5. 定义__eq__后对象突然不可哈希


❌ 只写了__eq__,结果对象放进set报TypeError: unhashable type。


✅ Python 3 中一旦类定义了__eq__而没有定义__hash__,__hash__会被置为None;需要可哈希时显式补上__hash__。


6.__add__里返回了NotImplementedError而不是NotImplemented


❌return NotImplementedError—— 这是把异常类当成值返回,运算结果会变成一个"异常类对象",完全错误。


✅return NotImplemented—— 这是值,告诉解释器转去尝试反向运算。


7. 鸭子类型下用hasattr判断时拼错方法名


❌if hasattr(obj, "arae"):—— 拼写错误永远返回False,还会掩盖真正的实现问题。


✅ 名字要与实现严格一致,或用try/except AttributeError(EAFP 风格)更稳妥。


8. 覆盖父类方法时把签名改了


❌ 父类def render(self, width),子类写成def render(self, width, height),多态调用时传一个参数直接TypeError。


✅ 覆盖方法保持与父类兼容的调用签名,多出来的能力用带默认值的参数补齐。


总结




多态的实现方式关键机制典型场景



鸭子类型只要求对象有同名方法松耦合的工具函数、脚本

继承 + 方法覆盖子类重写父类方法有明确层次关系的类型族

抽象基类abc.ABC+@abstractmethod需要强制实现接口

运算符多态特殊方法如__add__、__len__自定义数值/容器类型

参数多态函数对入参类型不做限制泛型风格的通用函数



多态的目的不是"用上某个语法",而是让调用方只依赖行为、不依赖具体类型。在 Python 里,鸭子类型是默认风格,继承和抽象基类是在需要约束时叠加的更强工具。判断一段代码是不是用了多态,看的是调用点是否只认方法名,而不是看它写没写class X(Y)。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询