
简介本资源是《Python Object-Oriented Programming》第四版的英文原版电子书由 Steven F. Lott 与 Dusty Phillips 合著Packt Publishing 于 2021 年出版。该书面向已具备 Python 语法基础、希望系统掌握面向对象设计并构建可维护应用与库的开发者也可作为高校相关课程的参考读物。全书围绕封装、继承、多态三大机制展开从类与实例、属性与方法等基础概念讲起逐步深入模块化组织、异常处理、单元测试与文档编写等工程实践并专门讨论版本控制、依赖管理、持续集成与社区协作等库维护要点配有大量可直接借鉴的代码范例。资源包共 1 个文件为 PDF 格式体积约 6.51MB单册完整收录正文内容便于在电脑或平板上检索与批注。目前已有 43 人学习关注。读者可从中获得从语法到架构的完整 OOP 知识路径理解如何用类层次组织复杂业务、用测试与异常机制提升健壮性并参照书中范例重构自己的项目代码。1. 先说清楚这本书到底在教你什么很多人学 Python 卡住的地方不是语法而是代码一多就乱成一锅粥变量满天飞改一处崩三处。《Python 面向对象编程第四版》要解决的就是这件事——教你把数据和操作数据的方法打包成一个个“对象”让程序从一堆零散的函数变成一群各司其职、能自己干活的小模块。书里从类和对象讲起一路覆盖继承与组合、异常处理、内置数据结构、常用设计模式最后落到自动化测试和并发编程。它适合已经能写循环和函数、但一遇到多文件项目就发怵的人也适合从别的语言转过来、想看清 Python 独有写法的人。完全零基础也不用怕前六章几乎是重新讲“什么是对象”。2. 为什么新手也该先搞懂面向对象从“能跑”到“能改”2.1 函数堆成山的那一刻就是该学面向对象的时候先别急着翻书回想一下自己写过的脚本。最开始可能是三十行读一个表格算几个数打印结果。两个月后老板说再加几个字段你又加了二十行。半年后这个文件变成八百行里面有十几个名字含糊的函数几个全局变量在函数之间来回传递还有一个变量在三个地方被修改你根本不知道哪一次修改是“最新”的。这时候你打开这个文件会有一个很具体的感受不敢改。因为任何一处改动都可能牵连别处而你没有精力把整条链路在脑子里跑一遍。这种恐惧不是能力问题是代码结构的问题。函数式写法把“数据”和“操作”拆开了数据在函数之间传递操作散落在各处程序越大这种分离带来的成本就越高。面向对象给出的解法很朴素把“跟着同一份数据转的那批操作”打包在一起让数据和操作住进同一个盒子。这样你改这个盒子内部的时候只要盒子对外的口子不变外面就不用动。这就是可维护性的具体含义不是什么抽象的大词。《Python 面向对象编程第四版》整本书都在讲这件事的不同侧面怎么打包类和对象、怎么复用继承和组合、怎么让错误可控异常、怎么让代码结构更稳设计模式、怎么确认自己没改坏测试。前六章是地基不懂地基直接看后面翻车概率极高——这是很多人的血泪经验。2.2 类和对象到底是什么用“奶茶店点单”讲明白官方定义通常是“类是对象的蓝图对象是类的实例”。这句话没错但它对新手基本等于没说。换个比喻。你走进一家奶茶店墙上有一张菜单写着“珍珠奶茶可选杯型、甜度、冰量价格 基础价 加料价”。这张菜单就是类它规定了这类东西长什么样、能做什么但它不能喝。你点了一杯“中杯、三分糖、少冰、加珍珠16 元”。这张具体的订单就是对象也叫实例它有自己具体的状态是可以真正拿来用的东西。订单上“三分糖”“中杯”这些信息就是属性描述这个对象现在什么样。订单上“可以加料”“可以算总价”这些能力就是方法描述这个对象能做什么动作。再用程序的话说一遍类是你写的一段代码定义了一组属性和方法对象是运行时真正创建出来的、有具体数据的那一个属性就是这个对象身上挂着的变量比如self.price方法就是这个对象能调用的函数比如line.subtotal()。为什么这套东西有威力因为“每天有三千杯奶茶”的时候你不需要写三千份菜单只写一份类然后创建三千个对象每个对象各自记自己的甜度。数据被分开了而处理逻辑只写了一遍。这是新手最容易立刻感受到的好处。还有两个常被提起的词顺便用这个比喻带过去。封装就是顾客不需要知道后厨怎么煮珍珠只面对菜单点单代码上就是外部只调用方法不直接翻看内部变量。继承就是“果茶菜单可以复用奶茶菜单里‘杯型、甜度、冰量’这些通用部分再加自己的水果配置”代码上就是子类拿父类现成的东西只写差异。这两个概念第 3 章会动手写出来先有个印象就够。提示一开始别纠结“我的类设计得对不对”。新手阶段能写出一个把数据和动作装在一起的类就已经跨过门槛了设计好不好是练出来的。2.3 这本书的路线图四段式从概念到能上线的代码这本书一共十四章结构其实很清楚分成四段。新手如果不清楚这个结构很容易第一章看两页就弃觉得“怎么全是概念”。第一段是第 1 到第 6 章核心概念。第 1 章讲对象、属性、行为、封装、继承、组合这些词到底指什么第 2 章真正开始写 Python 类同时引入类型提示type hints、模块和包第 3 章讲类之间的相似关系也就是继承、多继承、多态第 4 章讲异常处理怎么让程序在数据脏的时候不直接崩第 5 章回答一个很实际的问题——什么时候该用面向对象什么时候不该第 6 章讲抽象基类和运算符重载属于进阶但非常提气的部分。第二段是第 7 到第 9 章回头把 Python 自带的家伙什儿用面向对象的视角重新看一遍元组、命名元组、dataclass、字典、列表、集合、队列然后是字符串、正则、文件路径、JSON 序列化。这一段特别适合新手因为它不讲新概念而是把你天天用的东西讲透你会频繁产生“原来是这么回事”的感觉。第三段是第 10 到第 12 章设计模式。迭代器模式、装饰器、观察者、策略、命令、状态、单例再往后是适配器、外观、享元、抽象工厂、组合、模板。这里的关键是别背名字要理解“这个模式解决的是哪一类反复出现的问题”。第四段是第 13、14 章测试与并发。用 unittest 和 pytest 写自动化测试然后是线程、多进程、futures、异步 IO。贯穿全书还有一个案例一个简单的手写数字识别式的分类器用的是“找最相似的几个邻居来投票”的思路。它不是玩具每章都会在这个案例上新增一层功能你跟着走完会看到一个程序怎么从几行代码长成一个有结构的小系统。这种一以贯之的案例对新手理解“为什么要有这些结构”帮助极大比看十个互相独立的示例强。3. 第一步怎么迈跟着写第一个能跑的类3.1 准备环境三样东西就够了不要被“环境配置”这四个字吓到入门阶段你只需要三样第一一个 Python 解释器。装 3.9 或更新的版本都行这本书的示例用到类型提示版本太老会报错。第二一个能写代码的编辑器用你顺手的就行。第三一个干净的虚拟环境。这一步很多新手跳过然后过两个月发现装了一堆库互相打架后悔药都没得吃。在命令行里执行# 在项目文件夹下创建一个独立的 Python 环境 python -m venv .venv # 激活它Windows .venv\Scripts\activate # 激活它macOS / Linux source .venv/bin/activatevenv是 Python 自带的模块不需要额外安装。.venv是给这个环境起的名字你可以随便改激活之后命令行前面会出现(.venv)说明你后续装的库只会装进这个文件夹里不会污染系统里的 Python。项目不要了把这个文件夹删掉就干净了。注意每次打开新的命令行窗口都要重新激活一次虚拟环境这是新手最常忘记的一步忘了就会出现“我明明装了库怎么还报 ModuleNotFoundError”。3.2 第一个类把数据和动作装进同一个盒子我们从奶茶店那个比喻直接落地。新建一个文件order.py写第一个类# order.py # 订单里的一行记录商品名、单价、数量并能算出小计 class OrderLine: def __init__(self, name: str, price: float, quantity: int 1): # __init__ 是创建对象时自动执行的方法用来把初始数据挂到对象上 self.name name # 属性商品名 self.price price # 属性单价 self.quantity quantity # 属性数量默认 1 def subtotal(self) - float: # 方法算这一行的小计 return self.price * self.quantity if __name__ __main__: line OrderLine(笔记本, 12.5, 3) print(line.name, line.subtotal())一段一段看。class OrderLine:就是定义了一个类名字习惯用大驼峰。__init__是初始化方法创建对象的时候自动跑一次它的第一个参数self指的是“正在被创建的这个对象本身”。self.name name的意思是把传进来的参数name挂到这个对象身上以后通过line.name就能取到。这就是属性的诞生过程。def subtotal(self)是方法。注意它也必须带self因为它要读这个对象自己的price和quantity。不带self的话这个方法不知道自己算的是哪一行的账。参数说明name: str里的: str是类型提示它不影响运行只是告诉读代码的人和你用的编辑器“这里应该是个字符串”。quantity: int 1里的 1是默认值创建对象时不传数量就按 1 算。类型提示是这本书第 2 章重点讲的特性养成写它的习惯能少踩很多坑。跑一下python order.py输出笔记本 37.5。到这里你已经完成了从“函数”到“对象”的第一次跨越数据和算这个数据的动作现在住在一起了。再往前推一步用dataclass写同一件事代码会短很多from dataclasses import dataclass dataclass class OrderLine: name: str price: float quantity: int 1 def subtotal(self) - float: return self.price * self.quantitydataclass是一个装饰器它会自动帮你生成__init__、__repr__这些样板方法。你只需要写字段和方法重复代码被干掉了。这本书第 7 章专门讲这个新手提前看到不用怕理解成“Python 帮你写好了__init__”就行。什么时候用普通类、什么时候用 dataclass书里有明确的判断标准别自己拍脑袋。3.3 第二个例子继承与组合让代码不用重复抄单个类不够用。现实里“订单”是由“多个行”组成的这层关系用组合表达一个对象里面装着另一些对象。# 在 order.py 里接着写 class Order: def __init__(self, linesNone): # lines 是可选的不传就创建一个空列表注意不要写成 lines[] self.lines list(lines or []) def add(self, line: OrderLine) - Order: self.lines.append(line) return self # 返回自己方便链式调用 def total(self) - float: # 把每一行的小计加起来 return sum(line.subtotal() for line in self.lines)这里有个新手必踩的坑def __init__(self, lines[])。这样写所有对象会共享同一个列表你往 A 订单里加商品B 订单也莫名其妙多了一件。所以正确写法是先写None再在函数体里创建新的列表。return self是为了支持Order().add(a).add(b)这种连续调用不用也无所谓属于风格问题。跑一下order Order() order.add(OrderLine(笔记本, 12.5, 3)).add(OrderLine(钢笔, 30, 1)) print(order.total()) # 67.5接下来看继承。假设店里搞活动某些商品打八折class DiscountedLine(OrderLine): def __init__(self, name: str, price: float, quantity: int 1, rate: float 0.8): super().__init__(name, price, quantity) # 先让父类把通用字段处理好 self.rate rate # 再补上子类独有的字段 def subtotal(self) - float: # 覆盖父类的同名方法加上折扣 return super().subtotal() * self.rateclass DiscountedLine(OrderLine)表示“打折行是一种订单行”括号里是父类。super().__init__(...)调用父类的初始化把name、price、quantity交给父类处理避免重写一遍。subtotal方法同名重写这叫覆盖它的好处是外部代码完全不用改order.add(DiscountedLine(...))之后order.total()自动按折扣算因为total里调的是每一行自己的subtotal()。这就是多态的实际价值——调用方不需要知道具体是哪一类行只要它会算小计就行。新手容易在这里迷失继承不是“看起来像就能继承”。判断标准只有一条子类是不是父类的一种特殊版本。笔记本打折行是订单行成立。而“订单”和“订单行”之间不是继承是组合。这个区分想不清楚代码结构很快就会变形第 3 章和第 5 章来回讲的就是这个判断。3.4 跑完之后自检三个问题确认你真的懂了代码跑通不代表学会。第一个例子跑完之后停下来问自己三个问题。第一个如果我要给每一行加一个“备注”字段需要改几个地方答案是最多改两处——init里加一行赋值type hint 那里加个类型标注其他地方不用动。这就是封装带来的好处变化被限制在一个盒子里。第二个如果我删掉Order.total方法改成在外面写一个函数sum(order.lines)行不行行这也不是错。第 5 章讨论的就是“什么时候值得写成类”如果一个类只有一个方法、只被调用一次那确实不如直接写函数。面向对象不是越用越好是“该用的时候用”。第三个如果quantity传进来是负数会发生什么答案是算出来的小计也是负数程序一声不吭地跑完了然后你在报表里看到一堆负数。这就是第 4 章异常处理的入口数据不合规的时候程序应该主动喊停而不是默默算出一个错数字。这三个问题如果你都能答上来说明你已经站在面向对象的门口了。这本书后面的所有内容都是在这三扇门后面继续走。4. 新手最容易翻车的五个坑排查清单4.1 坑一以为“用了类”就是面向对象现象写了十几个类每个类里除了__init__以外什么都没有全部逻辑还是堆在外面几个大函数里最后代码比不用类的时候还长绕得更晕。原因把类当成了“数据容器”来用只放属性不放方法等于把数据打包了一层壳但没有把“跟着数据转的那批逻辑”一起搬进去。原本散在函数里的判断和计算现在散在函数和类之间反而更乱。解决判断一个类该不该有某个方法看它动不动这组数据。如果一段逻辑全是围绕line.price和line.quantity在做运算那它就该是line的方法。改完之后外部代码从“取数据、算、塞回去”变成“让对象自己算”这才是面向对象省下来的那部分力气。4.2 坑二self忘了写或者把方法写成了模块函数现象调用line.subtotal()的时候报TypeError: subtotal() takes 0 positional arguments but 1 was given明明参数都写对了。原因方法定义时漏了self。Python 在调用实例方法时会自动把对象本身作为第一个参数传进去所以定义时必须留出这个位置。def subtotal():这种写法Python 会认为它不接受参数结果你通过对象调用时多塞了一个就报错。解决实例方法的第一个参数固定写self并且第一个参数名可以改但不要改改了别人读代码会困惑。另外如果你发现某个方法压根用不到self也不该写成def foo(self)塞在那里把它改成模块级函数或者在书上第 5 章讲的场景里用staticmethod这才是干净的做法。4.3 坑三为了让子类复用代码硬套继承现象写着写着Manager继承了EmployeeEmployee又继承了Person然后为了复用某个字段又让Order继承了Product。最后调一个方法得往上翻四五层父类才能找到它到底在哪定义改一处崩一片。原因把继承当成了“复制代码的快捷方式”。继承的本意是表达“是一种”的关系不是“我需要这两个字段”。用它来共享代码层级会越来越深而深继承是最难维护的结构之一。解决能用组合就别用继承。订单包含若干订单行用组合打折行是一种订单行用继承。分辨方法是造个句子“A 是一种 B”读起来通顺才考虑继承。读起来别扭就换成“A 里有一个 B”用组合。这本书第 3 章给了很多这样的对照例子值得反复看。提示继承层级不要超过两层。超过两层说明你大概率在用它偷懒不是在用它建模。4.4 坑四异常捕获写成“一把梭”现象程序里到处都是try: ... except Exception: pass。看起来不报错了但实际上数据错了你也不知道等到下游发现数字全不对的时候已经过去两个星期查都查不出来。原因怕报错所以把所有异常一口吞掉。except Exception会捕获包括拼写错误、类型错误在内的一切问题pass又不做任何处理等于把错误从“当场炸”变成“以后炸”。解决只捕获你知道怎么处理的那一类异常其余的让它往上冒。书里第 4 章讲的做法是给业务问题定义一个自己的异常类比如InvalidQuantity然后在数据校验的地方主动raise。这样报错信息一眼就能看出是业务规则被违反而不是一串看不懂的堆栈。捕获的时候写except InvalidQuantity as e:在异常里带上具体数值事后排查会轻松很多。class InvalidQuantity(ValueError): 数量必须为正整数时抛出 pass def check_quantity(q: int) - None: if q 0: raise InvalidQuantity(f数量必须是正整数收到 {q})这个类只做一件事但它的名字本身就把“哪里出问题”讲清楚了。新手往往觉得自定义异常是多余的改上两三次代码之后就会反过来感谢它。4.5 坑五不写测试改一行崩三处现象加了一个小功能跑了一遍看起来正常就交上去了。两天后接到反馈说三个月前做的一个报表页面数据错了你的改动把那条路径弄坏了而这条路径你根本不知道存在。原因手动点几下只能覆盖你想到的路径而程序里的路径比你想到的多得多。没有自动化测试你每次改动都是在赌博。解决从最小的测试写起一个判断就够。第 13 章讲 pytest写法极简一个test_开头的函数里面一个assert。你不需要一次覆盖全部先把最容易出错的三个计算点写死以后每改一次代码就跑一遍。这件事的前期成本大概半小时省下来的排查时间是一个数量级。# test_order.py from order import Order, OrderLine, DiscountedLine def test_total_with_plain_lines(): order Order([OrderLine(笔记本, 12.5, 3), OrderLine(钢笔, 30, 1)]) assert order.total() 67.5 def test_discount_applies(): order Order([DiscountedLine(笔记本, 12.5, 2)]) assert order.total() 20.0运行pytest两个通过就是绿灯。参数说明pytest会自动发现当前目录下test_开头的文件和函数不需要你手动注册。断言失败时它会打印实际值和期望值看那个数字就知道哪里算错了。5. 学完核心章节之后下一步往哪走很多人卡在这里前六章看完了后面九章每一章都像新世界不知道先扑哪个。我踩过的坑是按顺序硬啃设计模式结果看完只会背名字遇到问题还是写不出来。后来调整了顺序效率完全不一样。建议的顺序是这样的先把第 7 章的数据结构吃透因为元组、dataclass、字典、集合这些是你每天都在用的东西学完立刻能用上正反馈最快。然后跳到第 13 章学测试越早越好测试是后面所有改动的安全网没有它你学设计模式的时候不敢动自己的代码。再回头学第 10 章的迭代器与生成器这一章是 Python 里最实用的一招处理大文件、分批读数据都靠它。最后才是第 11、12 章的模式那时候你已经有了自己的项目痛点看到某个模式会立刻对上号而不是空转。时间安排上每周一个主题比较现实周次主题对应章节每周产出第 1 周类与对象、属性与方法1、2一个能跑的小工具类第 2 周继承、组合、多态3把上一步的工具类重构一次第 3 周异常与数据校验4、5给工具类加上输入检查第 4 周内置数据结构7用 dataclass 重写数据部分第 5 周自动化测试13三个以上的测试用例第 6 周迭代器与生成器10改成逐行处理不再一次读入内存这个表的意义不在于严格照做而在于给你一个“每周都有东西能跑出来”的节奏。看书最怕的就是连续两周没有产出一旦断了就很难接上。最后分享一个我自己的习惯也是踩了太多坑之后养成的每次想加一个新功能之前先给现有代码补一个测试看着它变绿再动手改。这个顺序听起来多余但它把“我改坏了没有”这个问题从“凭感觉”变成了“看颜色”。从那以后我每次重构自己的小项目都强制走一遍“先跑测试、再改代码、改完再跑一遍”的流程一次都没省过。省下来的排查时间比写测试的时间多得多。入门这件事最难的不是概念本身是迈出第一脚。从OrderLine那个二十行的类开始写写完你会发现面向对象没那么玄。希望帮到你。本文还有配套的精品资源点击获取