面向对象高级-多态与鸭子类型

面向对象高级-多态与鸭子类型

2026年9月20日·#编程学习/python学习笔记Python/面向对象·1195 字 6 分钟
浏览量加载中...
AI 摘要

多态的含义与写法(父类类型参数接收任意子类对象),以及"关注行为不关注类型"的鸭子类型

多态是什么#

多态是指同一个方法,具有不同的形态、行为、表现

class Car:
def charge(self):
print('正在补充燃料...')
class FuelCar(Car):
def charge(self):
print('燃油汽车正在加油...')
class ElectricCar(Car):
def charge(self):
print('电动汽车正在充电...')
# 函数参数类型声明为父类
def handle_charge(car: Car):
car.charge()
handle_charge(FuelCar()) # 燃油汽车正在加油...
handle_charge(ElectricCar()) # 电动汽车正在充电...

关键写法:定义函数时参数类型指定为父类类型,执行时传入不同的子类对象,同一句 car.charge() 就表现出不同形态。

Tip

多态的价值在于调用方不用关心具体是哪种子类。以后新增”氢能车”,只要它也继承 Car 并实现 charge(),上面的 handle_charge 一行都不用改。

鸭子类型(Duck Typing)#

如果它走路像鸭子、叫起来像鸭子,那么它就是鸭子。

在鸭子类型中,我们关注的是对象的行为(它有什么方法),而不是对象的类型(它是什么类)。

class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def swimming(self):
print(f'{self.age} 岁的 {self.name} 正在游泳...')
class Duck:
def __init__(self, name, age):
self.name = name
self.age = age
def swimming(self):
print(f'{self.age} 岁的 {self.name} 正在游泳...')
class Pig:
def __init__(self, name, age):
self.name = name
self.age = age
def swimming(self):
print(f'{self.age} 岁的 {self.name} 正在游泳...')
def go_swimming(duck): # 参数没写类型,只要"会游泳"就行
duck.swimming()
if __name__ == '__main__':
go_swimming(Dog("旺财", 4))
go_swimming(Duck("唐老鸭", 2))
go_swimming(Pig("佩奇", 1))
多态鸭子类型
关注点父类 + 子类的继承体系有没有那个方法(行为)
是否需要继承需要(子类继承父类)不需要
好处统一调用入口,便于扩展更灵活,任何对象只要能”做这件事”就能传进来
Note

鸭子类型也可以看作多态的一种体现:关注的是对象的行为(有什么方法),而不是对象的类型(无需继承关系)。Python 作为动态类型语言,天然支持鸭子类型。

相关#

练习题#

一、回忆填空(写完再展开对答案)#

  1. 多态是指同一个方法,具有不同的 ____、行为、表现
  2. 实现多态的常见写法:函数参数类型指定为 ____ 类型,执行时传入不同的 ____ 对象
  3. 鸭子类型的说法是”如果它走路像____、叫起来像____,那么它就是鸭子”
  4. 鸭子类型关注的是对象的 ____(有什么方法),而不是对象的 ____(是什么类)
  5. 鸭子类型 ____(需要/不需要)存在继承关系
  6. 多态的好处:调用方不用关心具体的 ____,新增子类时调用代码 ____(要/不用)改
填空答案(做完再点开)
  1. 形态 2. 父类 / 子类 3. 鸭子、鸭子 4. 行为 / 类型 5. 不需要 6. 子类类型 / 不用

二、裸写题#

  • 2-1 体会多态 定义 Payment 父类(方法 pay())和 WechatPayAliPay 两个子类各自重写 pay();写一个函数 checkout(payment: Payment) 调用它,分别传入两种支付对象。

    提示(先自己想,实在想不出再点开)

    一级 · 思路:父类定义”能支付”,子类各自实现具体支付方式 二级 · 方法:函数形参写父类类型 payment: Payment,函数体只写 payment.pay() 三级 · 骨架class WechatPay(Payment): 然后重写 pay

  • 2-2 体会鸭子类型 写一个 start_game(device) 函数,参数不限制类型,只要对象有 play() 方法就能传进来——分别传手机、电脑、掌机三个互不继承的类。

    提示

    一级 · 思路:三个类毫无血缘关系,但都会 play 二级 · 方法def start_game(device): device.play() 三级 · 骨架:如果传进去的对象没有 play(),会报 AttributeError——这正说明它检查的是”有没有方法”

  • 2-3 两者对比 在 2-2 的基础上,故意传一个没有 play() 方法的对象,观察报错;再说出多态与鸭子类型的区别。

    提示

    一级 · 思路:报错信息能帮你理解”鸭子类型靠行为兜底” 二级 · 方法:用 try/except AttributeError 捕获并打印友好提示 三级 · 骨架:多态靠继承体系统一入口;鸭子类型只要方法名对得上就行

参考答案(做完再点开)
# 2-1 多态:靠继承体系
class Payment:
def pay(self):
print("正在支付...")
class WechatPay(Payment):
def pay(self):
print("微信支付 100 元")
class AliPay(Payment):
def pay(self):
print("支付宝支付 100 元")
def checkout(payment: Payment):
payment.pay()
checkout(WechatPay())
checkout(AliPay())
# 2-2 / 2-3 鸭子类型:不需要继承关系
class Phone:
def play(self):
print("手机:启动手游")
class Laptop:
def play(self):
print("电脑:启动端游")
class Console:
def play(self):
print("掌机:启动主机游戏")
class Fridge: # 没有 play 方法
pass
def start_game(device):
device.play()
start_game(Phone())
start_game(Laptop())
start_game(Console())
try:
start_game(Fridge())
except AttributeError as e:
print("启动失败:", e) # 'Fridge' object has no attribute 'play'

评论区

[ 标签 ]
# AI37# AI 编程2# AI工具1# Ajax2# Apifox1# AstrBot3# Astro2# CC Switch1# CDN2# Claude Code1# claudecode2# ClaudeCode1# Cloudflare2# CloudFlare2# CloudFlare-ImgBed3# coc3# CSS6# DeepSeek6# deepseek2# DELETE1# Docker1# EdgeOne3# Gist1# git1# GitHub1# hexo-circle-of-friends1# HTML6# HTTP5# ImageManager1# Java23# java13# JavaScript5# JDBC3# JSON2# JUnit1# LangChain25# Logback1# Maven6# Muse Spark1# Mybatis1# MyBatis4# MySQL28# MySql1# NapCat1# Node.js1# obsidian2# Obsidian5# OpenCode4# ORM1# PathVariable1# PicGo1# PyCharm1# Python65# RequestBody1# RequestMapping1# RESTful风格1# skills1# Slf4j1# SpringBoot11# SQL2# Streamlit5# Svelte2# TailwindCSS1# Telegram3# Tlias2# Vercel1# vscode2# Vue7# Waline3# WebDAV1# Web基础6# Web开发6# WinSCP1# YAML1# 三层架构1# 中二宣言1# 书籍1# 使用文档10# 写作1# 函数2# 刷步数1# 前端32# 动态1# 动漫1# 包1# 单词2# 博客7# 博客工作流1# 博客开发2# 参数接收1# 友链1# 反思2# 图床6# 地图1# 备份2# 大模型1# 奇思妙想1# 存储1# 学习方法6# 学校1# 宝塔面板3# 宝宝10# 对象1# 导航栏1# 工具2# 开发1# 开发工具1# 开发规范1# 开心1# 异常处理1# 影视2# 微信1# 性能优化2# 总结1# 想法15# 感受1# 感悟11# 指南1# 提示词工程1# 插件5# 故障排除1# 效率工具2# 教程10# 数据分析9# 数据库27# 数据结构1# 文件操作2# 斩神1# 日常92# 日志框架1# 朋友圈1# 朱元璋1# 模块1# 模板1# 正则表达式2# 测试1# 游戏2# 爬虫7# 生活迁移1# 电影2# 电脑1# 碎碎念1# 视觉识别1# 类1# 类型注解1# 网络基础2# 网络教室1# 羊毛2# 脚本2# 脚本工具1# 自动化2# 蓝奏云1# 订阅推荐2# 记录2# 评论系统1# 词根1# 词缀1# 说说1# 足迹1# 跑步2# 路径参数1# 转载2# 运动1# 部落冲突1# 配置1# 随机图1# 面向对象5# 音乐3# 音标1# 饮食1# 驼峰命名1# 高德地图1
[ 公告 ]

如果你喜欢,那么欢迎来到我的世界!

了解更多
[ 音乐 ]
封面

音乐

暂未播放

0:000:00
暂无歌词
找不到相关结果。
[ contents ]
[ 全部文章 ]