编程学习网 > 编程语言 > Python > Python 的多态,其实比你想象的要简单
2026
07-01

Python 的多态,其实比你想象的要简单


我记得我学 Python 多态那会儿,教材上来就是"继承""运行时绑定"。概念堆概念,看完一章,脑子里依然一团浆糊。

后来在实际项目里踩过几次坑,才搞明白——Python 的多态压根不需要这些花架子。核心逻辑很简单:一个对象能不能用,取决于它有没有你要调的那个方法。 类型是什么、有没有继承关系、有没有实现某个ABCPython 根本不在意。

这种风格有个名字,叫鸭子类型(Duck Typing)。名字来自一句俚语:

"If it walks like a duck and quacks like a duck, it must be a duck."

翻译成代码就是:有 pay 方法,就是能付款的。不管这对象是 Alipay 还是 WechatPay 还是 BankCard

def process_payment(p, amount):
    p.pay(amount)

三行代码。没有继承关系声明,没有类型约束。是什么类型不重要,有 pay 方法就能跑。

Java 程序员看到这里可能心里就发毛了——这代码万一传了个没 pay 方法的对象进来怎么办?

确实会炸:

class Cash:
    def give_money(self, amount):
        print(f"现金支付了 {amount} ")
process_payment(Cash(), 100)
# AttributeError: 'Cash' object has no attribute 'pay'

Cash 类的方法名是 give_money,不是 pay。运行到 p.pay(amount) 这一行才报错。小项目还好排查,几百行的代码量堆起来,错误出现在深层调用链里的时候,排查起来的确会挺折腾人。

这其实是鸭子类型的代价——把类型检查推迟到运行时

实际开发中,可以采取下面几种方法来兜底。

第一种,用 hasattr 做防御性检查。 多花两行代码,报错位置从深层调用链提前到函数入口:

def process_payment(p, amount):
    if not hasattr(p, 'pay'):
        raise TypeError(f"对象必须支持 pay 方法,当前类型{type(p).__name__}")
    p.pay(amount)

其实相比用 hasattr 做防御性检查,用try...except异常处理更直观、易于理解。

def process_payment(p,amount):
    try:
        p.pay(amount)
    except AttributeError:
        ...

无需管对象 p 的类型是什么,其中有没有 pay() 方法,先干了再说,这其实更PythonicPython EAFP 风格——“请求原谅比请求许可要容易得多。

第二种,文档里说清楚参数协议。 这不是给 Python 看的,是给接下来接手你代码的人看的:

def process_payment(p, amount):
    """
    处理支付操作。
    参数 p 需要支持 pay(amount) 方法。
    """
    p.pay(amount)

第三种,用 Protocol 静态类型检查 Python 3.8 引入的这个东西,本质上是个"不强制执行"的接口——运行时不检查,但一般 IDE 能帮你提前发现类型不匹配的问题:

from typing import Protocol
class Payable(Protocol):
    def pay(self, amount: float) -> None: ...
def process_payment(p: Payable, amount: float) -> None:
    p.pay(amount)

对大项目来说,Protocol 的收益很实在。写代码的时候 IDE 能提示"这个对象没有 pay 方法",可以在运行时报错前给出提示。

继续来点更实际的。鸭子类型不只是躺在教程里的一个概念,其在 Python 标准库里的实际应用随处可见。

最常见的例子是迭代协议。for 循环能遍历列表、字典、字符串、文件对象——这些类型之间没有任何继承关系,共同点是有 __iter__ 方法。想让自定义对象能放进 for 循环,实现 __iter__ 就行,不需要继承任何基类:

class Range:
    def __init__(self, start, end):
        self.start = start
        self.end = end
    def __iter__(self):
        current = self.start
        while current < self.end:
            yield current
            current += 1
for i in Range(0, 5):
    print(i)  # 0, 1, 2, 3, 4

with 语句也是这个思路。要求对象实现 __enter__  __exit__,文件、数据库连接、锁——全都能用,不需要它们有继承关系。

鸭子类型最大的价值,在实际业务的扩展场景里更能体现得出来。(其实就是多态)

做电商系统的时候,支付方式最初只支持支付宝,随后慢慢扩展支付方式加上了微信、银联、加密货币等等。不用多态的话,订单处理代码会越来越长:

if payment_type == 'alipay':
    Alipay().pay(amount)
elif payment_type == 'wechat':
    WechatPay().pay(amount)
elif payment_type == 'unionpay':
    BankCard().pay(amount)
elif payment_type == 'bitcoin':
    Bitcoin().pay(amount)

每加一种支付方式就要改这段代码,加个判断分支。改的人不一定是写这段代码的人,改完还要回归测试,挺容易出 bug 的。

用多态的思路,新增支付方式只需要加一个支持 pay 方法的类:

def pay_order(payment_method, amount):
    payment_method.pay(amount)

调用代码不用动。新增功能不触碰已有逻辑,测试也只针对新加支付方式测试即可——这就是开闭原则,也是多态在真实项目里的重要意义。

总结

Python 的多态不需要继承关系,靠鸭子类型就够了。它把类型检查推迟到运行时,换来的是更少的样板代码和更灵活的扩展方式。大项目里配合 Protocol 和静态类型检查工具,灵活性和安全性完全可以兼得。

以上就是“Python 的多态,其实比你想象的要简单的详细内容,想要了解更多Python教程欢迎持续关注编程学习网。 

扫码二维码 获取免费视频学习资料

Python编程学习

查 看2022高级编程视频教程免费获取