news 2026/5/24 1:39:53

Day 15:【99天精通Python】面向对象编程(OOP)中篇 - 封装、继承与多态

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Day 15:【99天精通Python】面向对象编程(OOP)中篇 - 封装、继承与多态

Day 15:【99天精通Python】面向对象编程(OOP)中篇 - 封装、继承与多态

前言

欢迎来到第15天!

在昨天的课程中,我们学会了如何定义类和创建对象。但这只是 OOP 的冰山一角。面向对象编程之所以强大,归功于它的三大核心特性:封装 (Encapsulation)继承 (Inheritance)多态 (Polymorphism)

这三个词听起来很高大上,但实际上它们是为了解决我们在编程中遇到的实际问题:

  • 封装:为了保护数据,不让别人随意修改。
  • 继承:为了偷懒(复用代码),爸爸有的儿子不用再写一遍。
  • 多态:为了灵活,同一个指令让不同的对象做不同的事。

本节内容:

  • 私有属性与封装
  • 继承的基本语法
  • 方法重写 (Overriding)
  • super()函数
  • 多态与鸭子类型
  • 实战练习

一、封装 (Encapsulation):保护你的数据

1.1 公有 vs 私有

默认情况下,Python 类中的属性都是公有 (Public)的,在类的外部可以随意访问和修改。这有时候很不安全。

classAccount:def__init__(self,balance):self.balance=balance acc=Account(100)acc.balance=-999999# 外部可以直接修改,这太危险了!

为了保护数据,我们可以将属性定义为私有 (Private)。在 Python 中,只需要在属性名前加两个下划线__

1.2 私有属性的使用

classAccount:def__init__(self,balance):self.__balance=balance# 私有属性defget_balance(self):"""提供公开的方法获取余额"""returnself.__balancedefdeposit(self,amount):"""提供公开的方法修改余额,可以在这里加逻辑判断"""ifamount>0:self.__balance+=amountelse:print("金额无效")acc=Account(1000)# print(acc.__balance) # 报错!外部无法访问print(acc.get_balance())# 1000 (通过方法访问)acc.deposit(500)

原理:Python 实际上是将__balance改名为了_Account__balance(名称改写),虽然硬要访问也能访问,但君子协定我们要遵守规则。


二、继承 (Inheritance):子承父业

2.1 为什么要继承?

假设我们要写CatDog两个类,它们都有name属性,都会eat。如果分别写两个类,代码就重复了。
我们可以提取一个父类Animal,让CatDog去继承它。

2.2 定义继承

# 父类 (基类)classAnimal:def__init__(self,name):self.name=namedefeat(self):print(f"{self.name}正在吃饭...")defsleep(self):print(f"{self.name}正在睡觉...")# 子类 (派生类) 继承 AnimalclassDog(Animal):defbark(self):print("汪汪汪!")classCat(Animal):defmeow(self):print("喵喵喵!")# 测试dog=Dog("旺财")dog.eat()# 继承自父类的方法 -> 旺财 正在吃饭...dog.bark()# 子类独有的方法 -> 汪汪汪!

2.3 方法重写 (Overriding)

如果子类对父类的方法不满意,可以重新定义它。

classBird(Animal):defeat(self):print(f"{self.name}正在啄米吃...")# 重写父类的 eatbird=Bird("波利")bird.eat()# 波利 正在啄米吃...

2.4 super() 函数

在子类中,如果想调用父类的方法(特别是在__init__中),需要用到super()

classDog(Animal):def__init__(self,name,breed):# 调用父类的 __init__ 初始化 namesuper().__init__(name)# 再初始化子类独有的 breedself.breed=breed d=Dog("来福","哈士奇")print(f"{d.name}{d.breed}")

三、多态 (Polymorphism):一种接口,多种形态

3.1 什么是多态?

多态是指:不同的子类对象调用相同的父类方法,产生不同的执行结果。

classAnimal:defspeak(self):passclassDog(Animal):defspeak(self):print("汪汪!")classCat(Animal):defspeak(self):print("喵喵!")defmake_noise(animal_obj):"""这个函数接收任何 Animal 对象"""animal_obj.speak()d=Dog("A")c=Cat("B")make_noise(d)# 汪汪!make_noise(c)# 喵喵!

3.2 鸭子类型 (Duck Typing)

Python 是一种动态语言,它并不严格要求继承体系。
“如果它走起来像鸭子,叫起来像鸭子,那它就是鸭子。”

哪怕一个类没有继承Animal,只要它也有speak()方法,上面的make_noise函数照样能处理它。

classCar:defspeak(self):print("滴滴!")car=Car()make_noise(car)# 滴滴! (Python 不检查类型,只检查有没有这个方法)

四、实战练习

练习1:工资管理系统 (继承与多态)

  1. 定义父类Employee,属性name,方法get_pay()
  2. 定义子类FullTimeEmployee,属性salary(月薪),重写get_pay()返回月薪。
  3. 定义子类PartTimeEmployee,属性hours,rate(时薪),重写get_pay()返回hours * rate
  4. 编写一个函数计算公司所有员工的总工资。
classEmployee:def__init__(self,name):self.name=namedefget_pay(self):return0classFullTimeEmployee(Employee):def__init__(self,name,salary):super().__init__(name)self.salary=salarydefget_pay(self):returnself.salaryclassPartTimeEmployee(Employee):def__init__(self,name,hours,rate):super().__init__(name)self.hours=hours self.rate=ratedefget_pay(self):returnself.hours*self.rate# 统计总支出staffs=[FullTimeEmployee("Alice",6000),FullTimeEmployee("Bob",8000),PartTimeEmployee("Charlie",50,20)# 50小时 * 20元 = 1000]total_pay=sum(emp.get_pay()forempinstaffs)print(f"公司本月总工资支出:{total_pay}")# 15000

五、OOP 类图结构

我们可以用 Mermaid 来直观地展示继承关系。

继承

继承

Animal

+name

+eat()

+sleep()

Dog

+bark()

Cat

+meow()


六、常见问题

Q1:私有方法怎么定义?

和属性一样,方法名前加双下划线。例如def __secret_method(self):,只能在类内部被self.__secret_method()调用。

Q2:Python 支持多继承吗?

支持。class C(A, B):表示 C 同时继承 A 和 B。但这会带来复杂性(如菱形继承问题),初学者建议先掌握单继承。

Q3:isinstance是什么?

用来判断对象是否属于某个类。

d=Dog("A")print(isinstance(d,Dog))# Trueprint(isinstance(d,Animal))# True (因为Dog继承Animal)print(isinstance(d,Cat))# False

七、小结

OOP 三大特性

封装 Encapsulation

继承 Inheritance

多态 Polymorphism

私有属性 __attr

公开方法访问

class Child(Parent)

代码复用

super() 调用父类

同名方法,不同实现

鸭子类型

关键要点

  1. 封装:用__隐藏内部细节,提供安全接口。
  2. 继承:子类自动拥有父类的功能,super()是连接父子的桥梁。
  3. 多态:让代码更灵活,关注点从"是什么类型"转变为"能做什么"。

八、课后作业

  1. 图形计算器
    • 定义父类Shape,有一个方法area()返回 0。
    • 定义子类Circle(属性半径) 和Square(属性边长)。
    • 分别重写area()方法。
    • 创建一个列表包含不同的图形,遍历打印它们的面积。
  2. 游戏角色
    • 定义Hero类,包含hp(血量) 和attack()方法。
    • 定义Mage(法师) 和Warrior(战士) 继承Hero
    • 法师攻击时打印"释放火球",战士攻击时打印"挥舞大剑"。
    • 尝试给法师增加一个mp(魔法值) 属性,并在攻击时消耗魔法。

下节预告

Day 16:面向对象编程(OOP)下篇 - 魔术方法与类属性-__str__是什么?__add__是什么?静态方法又是什么?明天我们将探索类的更多高级玩法!


系列导航

  • 上一篇:Day 14 - 面向对象编程(上)
  • 下一篇:Day 16 - 面向对象编程(下)(待更新)
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/8 21:10:01

字符设备驱动内存管理最佳实践解析

字符设备驱动内存管理:从踩坑到精通的实战指南你有没有遇到过这样的情况?驱动写得好好的,一跑起来却莫名其妙地宕机;或者系统用着用着内存越来越少,最后直接 OOM(Out of Memory)崩溃。更离谱的是…

作者头像 李华
网站建设 2026/5/8 21:11:25

Multisim14自定义虚拟仪器创建:从零开始教程

从零打造专属测量工具:Multisim14自定义虚拟仪器实战指南你有没有遇到过这样的情况?在做电路仿真时,标准示波器只能看波形、万用表只能测直流——但你想分析谐波畸变率、想自动识别元件类型、甚至希望一键生成Bode图。这时候,Mult…

作者头像 李华
网站建设 2026/5/10 9:25:57

手把手教程:RISC-V指令集异常入口设置

手把手教你配置RISC-V异常入口:从原理到实战你有没有遇到过这样的情况?在调试一个裸机程序时,定时器中断就是不触发;或者一执行非法指令,CPU直接“跑飞”,连断点都抓不到?问题很可能出在——异常…

作者头像 李华
网站建设 2026/5/21 11:56:50

多层板中电镀+蚀刻实现互连的原理探究:系统学习

多层板互连的底层密码:电镀与蚀刻如何“编织”电路的立体神经 你有没有想过,一块指甲盖大小的手机主板,为何能承载数十亿晶体管的数据洪流?又是什么让高速信号在层层叠叠的铜箔之间精准穿行,不迷路、不串扰&#xff1f…

作者头像 李华
网站建设 2026/5/20 15:57:15

XDMA核配置与FPGA逻辑对接:实战案例

XDMA实战:从零打通FPGA到主机的高速数据链路 你有没有遇到过这样的场景? FPGA里跑着1GSPS的ADC数据流,处理得飞快,结果一到传给CPU就卡了——要么丢包,要么延迟高得没法实时分析。传统的PCIe开发又太难:协…

作者头像 李华