学习第 4 天:面向对象与异常处理
学习目标
完成本章学习后,你将能够:
- 使用
class定义 Python 类,理解__init__和self的运作机制 - 掌握单继承、多继承以及 MRO(方法解析顺序)
- 理解鸭子类型(Duck Typing)------Python 多态的核心哲学
- 熟练使用魔术方法(
__str__/__repr__/__eq__等)定制类行为 - 使用
dataclass大幅减少样板代码 - 掌握枚举(Enum)和抽象基类(ABC)
- 设计 Python 风格的异常体系(
try/except/else/finally+ 自定义异常) - 使用上下文管理器(
with+contextlib)管理资源 - 为《企业级 AI Agent 平台》构建领域模型和异常体系
为什么学习本章
作为 Java 架构师,你对面向对象已经烂熟于心------封装、继承、多态、接口、抽象类。但 Python 的 OOP 会给你带来"文化冲击":没有 private 关键字、没有接口(interface)、支持多继承、多态基于"长得像就可以"而非类型层级。
这不是 Python OOP 的"缺陷",而是刻意为之的设计选择------更灵活、更少样板代码、更符合 Python "我们都是成年人" 的哲学。理解这些差异,你才能真正写出 Pythonic 的 OOP 代码,而不是"Java 风格的 Python"。
异常处理同样如此。Python 没有 checked exception,异常链机制也与 Java 不同。掌握 Python 异常体系是写出健壮企业代码的前提。
本章在知识体系中的位置
#mermaid-svg-Fd0JA87hSXfTebgk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Fd0JA87hSXfTebgk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Fd0JA87hSXfTebgk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Fd0JA87hSXfTebgk .error-icon{fill:#552222;}#mermaid-svg-Fd0JA87hSXfTebgk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Fd0JA87hSXfTebgk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Fd0JA87hSXfTebgk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Fd0JA87hSXfTebgk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Fd0JA87hSXfTebgk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Fd0JA87hSXfTebgk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Fd0JA87hSXfTebgk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Fd0JA87hSXfTebgk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Fd0JA87hSXfTebgk .marker.cross{stroke:#333333;}#mermaid-svg-Fd0JA87hSXfTebgk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Fd0JA87hSXfTebgk p{margin:0;}#mermaid-svg-Fd0JA87hSXfTebgk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Fd0JA87hSXfTebgk .cluster-label text{fill:#333;}#mermaid-svg-Fd0JA87hSXfTebgk .cluster-label span{color:#333;}#mermaid-svg-Fd0JA87hSXfTebgk .cluster-label span p{background-color:transparent;}#mermaid-svg-Fd0JA87hSXfTebgk .label text,#mermaid-svg-Fd0JA87hSXfTebgk span{fill:#333;color:#333;}#mermaid-svg-Fd0JA87hSXfTebgk .node rect,#mermaid-svg-Fd0JA87hSXfTebgk .node circle,#mermaid-svg-Fd0JA87hSXfTebgk .node ellipse,#mermaid-svg-Fd0JA87hSXfTebgk .node polygon,#mermaid-svg-Fd0JA87hSXfTebgk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Fd0JA87hSXfTebgk .rough-node .label text,#mermaid-svg-Fd0JA87hSXfTebgk .node .label text,#mermaid-svg-Fd0JA87hSXfTebgk .image-shape .label,#mermaid-svg-Fd0JA87hSXfTebgk .icon-shape .label{text-anchor:middle;}#mermaid-svg-Fd0JA87hSXfTebgk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Fd0JA87hSXfTebgk .rough-node .label,#mermaid-svg-Fd0JA87hSXfTebgk .node .label,#mermaid-svg-Fd0JA87hSXfTebgk .image-shape .label,#mermaid-svg-Fd0JA87hSXfTebgk .icon-shape .label{text-align:center;}#mermaid-svg-Fd0JA87hSXfTebgk .node.clickable{cursor:pointer;}#mermaid-svg-Fd0JA87hSXfTebgk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Fd0JA87hSXfTebgk .arrowheadPath{fill:#333333;}#mermaid-svg-Fd0JA87hSXfTebgk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Fd0JA87hSXfTebgk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Fd0JA87hSXfTebgk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Fd0JA87hSXfTebgk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Fd0JA87hSXfTebgk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Fd0JA87hSXfTebgk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Fd0JA87hSXfTebgk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Fd0JA87hSXfTebgk .cluster text{fill:#333;}#mermaid-svg-Fd0JA87hSXfTebgk .cluster span{color:#333;}#mermaid-svg-Fd0JA87hSXfTebgk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Fd0JA87hSXfTebgk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Fd0JA87hSXfTebgk rect.text{fill:none;stroke-width:0;}#mermaid-svg-Fd0JA87hSXfTebgk .icon-shape,#mermaid-svg-Fd0JA87hSXfTebgk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Fd0JA87hSXfTebgk .icon-shape p,#mermaid-svg-Fd0JA87hSXfTebgk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Fd0JA87hSXfTebgk .icon-shape .label rect,#mermaid-svg-Fd0JA87hSXfTebgk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Fd0JA87hSXfTebgk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Fd0JA87hSXfTebgk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Fd0JA87hSXfTebgk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 第1章
第2章
第3章
第4章
OOP与异常
⬅ 当前
第5章
模块包与工程化
...
本章是 Python 基础阶段的最后一章。从下一章开始,你将进入 Python 工程化和高级特性阶段。
学习完成后能够解决哪些问题
- 为 Agent 平台定义 User、Conversation、Message、Agent 等核心领域模型
- 设计企业级异常体系(区分业务异常、系统异常、外部服务异常)
- 使用
dataclass替代手写样板代码,像 Lombok 一样减少 80% 的代码量 - 利用鸭子类型写出更灵活、更易测试的代码
- 理解为什么 LangChain 的源码中可以自由替换模型实现而无需修改调用代码
知识体系图
#mermaid-svg-9tIElU5sjQicMFFd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9tIElU5sjQicMFFd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9tIElU5sjQicMFFd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9tIElU5sjQicMFFd .error-icon{fill:#552222;}#mermaid-svg-9tIElU5sjQicMFFd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9tIElU5sjQicMFFd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9tIElU5sjQicMFFd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9tIElU5sjQicMFFd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9tIElU5sjQicMFFd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9tIElU5sjQicMFFd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9tIElU5sjQicMFFd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9tIElU5sjQicMFFd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9tIElU5sjQicMFFd .marker.cross{stroke:#333333;}#mermaid-svg-9tIElU5sjQicMFFd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9tIElU5sjQicMFFd p{margin:0;}#mermaid-svg-9tIElU5sjQicMFFd .edge{stroke-width:3;}#mermaid-svg-9tIElU5sjQicMFFd .section--1 rect,#mermaid-svg-9tIElU5sjQicMFFd .section--1 path,#mermaid-svg-9tIElU5sjQicMFFd .section--1 circle,#mermaid-svg-9tIElU5sjQicMFFd .section--1 polygon,#mermaid-svg-9tIElU5sjQicMFFd .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-9tIElU5sjQicMFFd .section--1 text{fill:#ffffff;}#mermaid-svg-9tIElU5sjQicMFFd .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-9tIElU5sjQicMFFd .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-9tIElU5sjQicMFFd .edge-depth--1{stroke-width:17;}#mermaid-svg-9tIElU5sjQicMFFd .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-9tIElU5sjQicMFFd .disabled,#mermaid-svg-9tIElU5sjQicMFFd .disabled circle,#mermaid-svg-9tIElU5sjQicMFFd .disabled text{fill:lightgray;}#mermaid-svg-9tIElU5sjQicMFFd .disabled text{fill:#efefef;}#mermaid-svg-9tIElU5sjQicMFFd .section-0 rect,#mermaid-svg-9tIElU5sjQicMFFd .section-0 path,#mermaid-svg-9tIElU5sjQicMFFd .section-0 circle,#mermaid-svg-9tIElU5sjQicMFFd .section-0 polygon,#mermaid-svg-9tIElU5sjQicMFFd .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-9tIElU5sjQicMFFd .section-0 text{fill:black;}#mermaid-svg-9tIElU5sjQicMFFd .node-icon-0{font-size:40px;color:black;}#mermaid-svg-9tIElU5sjQicMFFd .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-9tIElU5sjQicMFFd .edge-depth-0{stroke-width:14;}#mermaid-svg-9tIElU5sjQicMFFd .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-9tIElU5sjQicMFFd .disabled,#mermaid-svg-9tIElU5sjQicMFFd .disabled circle,#mermaid-svg-9tIElU5sjQicMFFd .disabled text{fill:lightgray;}#mermaid-svg-9tIElU5sjQicMFFd .disabled text{fill:#efefef;}#mermaid-svg-9tIElU5sjQicMFFd .section-1 rect,#mermaid-svg-9tIElU5sjQicMFFd .section-1 path,#mermaid-svg-9tIElU5sjQicMFFd .section-1 circle,#mermaid-svg-9tIElU5sjQicMFFd .section-1 polygon,#mermaid-svg-9tIElU5sjQicMFFd .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-9tIElU5sjQicMFFd .section-1 text{fill:black;}#mermaid-svg-9tIElU5sjQicMFFd .node-icon-1{font-size:40px;color:black;}#mermaid-svg-9tIElU5sjQicMFFd .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-9tIElU5sjQicMFFd .edge-depth-1{stroke-width:11;}#mermaid-svg-9tIElU5sjQicMFFd .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-9tIElU5sjQicMFFd .disabled,#mermaid-svg-9tIElU5sjQicMFFd .disabled circle,#mermaid-svg-9tIElU5sjQicMFFd .disabled text{fill:lightgray;}#mermaid-svg-9tIElU5sjQicMFFd .disabled text{fill:#efefef;}#mermaid-svg-9tIElU5sjQicMFFd .section-2 rect,#mermaid-svg-9tIElU5sjQicMFFd .section-2 path,#mermaid-svg-9tIElU5sjQicMFFd .section-2 circle,#mermaid-svg-9tIElU5sjQicMFFd .section-2 polygon,#mermaid-svg-9tIElU5sjQicMFFd .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-9tIElU5sjQicMFFd .section-2 text{fill:#ffffff;}#mermaid-svg-9tIElU5sjQicMFFd .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-9tIElU5sjQicMFFd .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-9tIElU5sjQicMFFd .edge-depth-2{stroke-width:8;}#mermaid-svg-9tIElU5sjQicMFFd .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-9tIElU5sjQicMFFd .disabled,#mermaid-svg-9tIElU5sjQicMFFd .disabled circle,#mermaid-svg-9tIElU5sjQicMFFd .disabled text{fill:lightgray;}#mermaid-svg-9tIElU5sjQicMFFd .disabled text{fill:#efefef;}#mermaid-svg-9tIElU5sjQicMFFd .section-3 rect,#mermaid-svg-9tIElU5sjQicMFFd .section-3 path,#mermaid-svg-9tIElU5sjQicMFFd .section-3 circle,#mermaid-svg-9tIElU5sjQicMFFd .section-3 polygon,#mermaid-svg-9tIElU5sjQicMFFd .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-9tIElU5sjQicMFFd .section-3 text{fill:black;}#mermaid-svg-9tIElU5sjQicMFFd .node-icon-3{font-size:40px;color:black;}#mermaid-svg-9tIElU5sjQicMFFd .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-9tIElU5sjQicMFFd .edge-depth-3{stroke-width:5;}#mermaid-svg-9tIElU5sjQicMFFd .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-9tIElU5sjQicMFFd .disabled,#mermaid-svg-9tIElU5sjQicMFFd .disabled circle,#mermaid-svg-9tIElU5sjQicMFFd .disabled text{fill:lightgray;}#mermaid-svg-9tIElU5sjQicMFFd .disabled text{fill:#efefef;}#mermaid-svg-9tIElU5sjQicMFFd .section-4 rect,#mermaid-svg-9tIElU5sjQicMFFd .section-4 path,#mermaid-svg-9tIElU5sjQicMFFd .section-4 circle,#mermaid-svg-9tIElU5sjQicMFFd .section-4 polygon,#mermaid-svg-9tIElU5sjQicMFFd .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-9tIElU5sjQicMFFd .section-4 text{fill:black;}#mermaid-svg-9tIElU5sjQicMFFd .node-icon-4{font-size:40px;color:black;}#mermaid-svg-9tIElU5sjQicMFFd .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-9tIElU5sjQicMFFd .edge-depth-4{stroke-width:2;}#mermaid-svg-9tIElU5sjQicMFFd .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-9tIElU5sjQicMFFd .disabled,#mermaid-svg-9tIElU5sjQicMFFd .disabled circle,#mermaid-svg-9tIElU5sjQicMFFd .disabled text{fill:lightgray;}#mermaid-svg-9tIElU5sjQicMFFd .disabled text{fill:#efefef;}#mermaid-svg-9tIElU5sjQicMFFd .section-5 rect,#mermaid-svg-9tIElU5sjQicMFFd .section-5 path,#mermaid-svg-9tIElU5sjQicMFFd .section-5 circle,#mermaid-svg-9tIElU5sjQicMFFd .section-5 polygon,#mermaid-svg-9tIElU5sjQicMFFd .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-9tIElU5sjQicMFFd .section-5 text{fill:black;}#mermaid-svg-9tIElU5sjQicMFFd .node-icon-5{font-size:40px;color:black;}#mermaid-svg-9tIElU5sjQicMFFd .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-9tIElU5sjQicMFFd .edge-depth-5{stroke-width:-1;}#mermaid-svg-9tIElU5sjQicMFFd .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-9tIElU5sjQicMFFd .disabled,#mermaid-svg-9tIElU5sjQicMFFd .disabled circle,#mermaid-svg-9tIElU5sjQicMFFd .disabled text{fill:lightgray;}#mermaid-svg-9tIElU5sjQicMFFd .disabled text{fill:#efefef;}#mermaid-svg-9tIElU5sjQicMFFd .section-6 rect,#mermaid-svg-9tIElU5sjQicMFFd .section-6 path,#mermaid-svg-9tIElU5sjQicMFFd .section-6 circle,#mermaid-svg-9tIElU5sjQicMFFd .section-6 polygon,#mermaid-svg-9tIElU5sjQicMFFd .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-9tIElU5sjQicMFFd .section-6 text{fill:black;}#mermaid-svg-9tIElU5sjQicMFFd .node-icon-6{font-size:40px;color:black;}#mermaid-svg-9tIElU5sjQicMFFd .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-9tIElU5sjQicMFFd .edge-depth-6{stroke-width:-4;}#mermaid-svg-9tIElU5sjQicMFFd .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-9tIElU5sjQicMFFd .disabled,#mermaid-svg-9tIElU5sjQicMFFd .disabled circle,#mermaid-svg-9tIElU5sjQicMFFd .disabled text{fill:lightgray;}#mermaid-svg-9tIElU5sjQicMFFd .disabled text{fill:#efefef;}#mermaid-svg-9tIElU5sjQicMFFd .section-7 rect,#mermaid-svg-9tIElU5sjQicMFFd .section-7 path,#mermaid-svg-9tIElU5sjQicMFFd .section-7 circle,#mermaid-svg-9tIElU5sjQicMFFd .section-7 polygon,#mermaid-svg-9tIElU5sjQicMFFd .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-9tIElU5sjQicMFFd .section-7 text{fill:black;}#mermaid-svg-9tIElU5sjQicMFFd .node-icon-7{font-size:40px;color:black;}#mermaid-svg-9tIElU5sjQicMFFd .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-9tIElU5sjQicMFFd .edge-depth-7{stroke-width:-7;}#mermaid-svg-9tIElU5sjQicMFFd .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-9tIElU5sjQicMFFd .disabled,#mermaid-svg-9tIElU5sjQicMFFd .disabled circle,#mermaid-svg-9tIElU5sjQicMFFd .disabled text{fill:lightgray;}#mermaid-svg-9tIElU5sjQicMFFd .disabled text{fill:#efefef;}#mermaid-svg-9tIElU5sjQicMFFd .section-8 rect,#mermaid-svg-9tIElU5sjQicMFFd .section-8 path,#mermaid-svg-9tIElU5sjQicMFFd .section-8 circle,#mermaid-svg-9tIElU5sjQicMFFd .section-8 polygon,#mermaid-svg-9tIElU5sjQicMFFd .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-9tIElU5sjQicMFFd .section-8 text{fill:black;}#mermaid-svg-9tIElU5sjQicMFFd .node-icon-8{font-size:40px;color:black;}#mermaid-svg-9tIElU5sjQicMFFd .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-9tIElU5sjQicMFFd .edge-depth-8{stroke-width:-10;}#mermaid-svg-9tIElU5sjQicMFFd .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-9tIElU5sjQicMFFd .disabled,#mermaid-svg-9tIElU5sjQicMFFd .disabled circle,#mermaid-svg-9tIElU5sjQicMFFd .disabled text{fill:lightgray;}#mermaid-svg-9tIElU5sjQicMFFd .disabled text{fill:#efefef;}#mermaid-svg-9tIElU5sjQicMFFd .section-9 rect,#mermaid-svg-9tIElU5sjQicMFFd .section-9 path,#mermaid-svg-9tIElU5sjQicMFFd .section-9 circle,#mermaid-svg-9tIElU5sjQicMFFd .section-9 polygon,#mermaid-svg-9tIElU5sjQicMFFd .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-9tIElU5sjQicMFFd .section-9 text{fill:black;}#mermaid-svg-9tIElU5sjQicMFFd .node-icon-9{font-size:40px;color:black;}#mermaid-svg-9tIElU5sjQicMFFd .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-9tIElU5sjQicMFFd .edge-depth-9{stroke-width:-13;}#mermaid-svg-9tIElU5sjQicMFFd .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-9tIElU5sjQicMFFd .disabled,#mermaid-svg-9tIElU5sjQicMFFd .disabled circle,#mermaid-svg-9tIElU5sjQicMFFd .disabled text{fill:lightgray;}#mermaid-svg-9tIElU5sjQicMFFd .disabled text{fill:#efefef;}#mermaid-svg-9tIElU5sjQicMFFd .section-10 rect,#mermaid-svg-9tIElU5sjQicMFFd .section-10 path,#mermaid-svg-9tIElU5sjQicMFFd .section-10 circle,#mermaid-svg-9tIElU5sjQicMFFd .section-10 polygon,#mermaid-svg-9tIElU5sjQicMFFd .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-9tIElU5sjQicMFFd .section-10 text{fill:black;}#mermaid-svg-9tIElU5sjQicMFFd .node-icon-10{font-size:40px;color:black;}#mermaid-svg-9tIElU5sjQicMFFd .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-9tIElU5sjQicMFFd .edge-depth-10{stroke-width:-16;}#mermaid-svg-9tIElU5sjQicMFFd .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-9tIElU5sjQicMFFd .disabled,#mermaid-svg-9tIElU5sjQicMFFd .disabled circle,#mermaid-svg-9tIElU5sjQicMFFd .disabled text{fill:lightgray;}#mermaid-svg-9tIElU5sjQicMFFd .disabled text{fill:#efefef;}#mermaid-svg-9tIElU5sjQicMFFd .section-root rect,#mermaid-svg-9tIElU5sjQicMFFd .section-root path,#mermaid-svg-9tIElU5sjQicMFFd .section-root circle,#mermaid-svg-9tIElU5sjQicMFFd .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-9tIElU5sjQicMFFd .section-root text{fill:#ffffff;}#mermaid-svg-9tIElU5sjQicMFFd .section-root span{color:#ffffff;}#mermaid-svg-9tIElU5sjQicMFFd .section-2 span{color:#ffffff;}#mermaid-svg-9tIElU5sjQicMFFd .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-9tIElU5sjQicMFFd .edge{fill:none;}#mermaid-svg-9tIElU5sjQicMFFd .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-9tIElU5sjQicMFFd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 第4章:OOP与异常
类与对象
class 定义
init 与 self
实例方法
类方法 staticmethod
属性系统
实例属性
类属性
property 装饰器
属性访问控制
OOP 核心
单继承
多继承与 MRO
鸭子类型 多态
魔术方法
dataclass
Enum 枚举
定义常量
自动赋值
字符串枚举
ABC 抽象基类
@abstractmethod
接口模拟
isinstance vs type
异常处理
try except else finally
自定义异常
异常链
raise from
上下文管理器
enter exit
contextlib
Python OOP 与 Java OOP 的核心差异:
#mermaid-svg-6Mi8kaGiNbGtIrZR{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6Mi8kaGiNbGtIrZR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6Mi8kaGiNbGtIrZR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6Mi8kaGiNbGtIrZR .error-icon{fill:#552222;}#mermaid-svg-6Mi8kaGiNbGtIrZR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6Mi8kaGiNbGtIrZR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6Mi8kaGiNbGtIrZR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6Mi8kaGiNbGtIrZR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6Mi8kaGiNbGtIrZR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6Mi8kaGiNbGtIrZR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6Mi8kaGiNbGtIrZR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6Mi8kaGiNbGtIrZR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6Mi8kaGiNbGtIrZR .marker.cross{stroke:#333333;}#mermaid-svg-6Mi8kaGiNbGtIrZR svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6Mi8kaGiNbGtIrZR p{margin:0;}#mermaid-svg-6Mi8kaGiNbGtIrZR .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6Mi8kaGiNbGtIrZR .cluster-label text{fill:#333;}#mermaid-svg-6Mi8kaGiNbGtIrZR .cluster-label span{color:#333;}#mermaid-svg-6Mi8kaGiNbGtIrZR .cluster-label span p{background-color:transparent;}#mermaid-svg-6Mi8kaGiNbGtIrZR .label text,#mermaid-svg-6Mi8kaGiNbGtIrZR span{fill:#333;color:#333;}#mermaid-svg-6Mi8kaGiNbGtIrZR .node rect,#mermaid-svg-6Mi8kaGiNbGtIrZR .node circle,#mermaid-svg-6Mi8kaGiNbGtIrZR .node ellipse,#mermaid-svg-6Mi8kaGiNbGtIrZR .node polygon,#mermaid-svg-6Mi8kaGiNbGtIrZR .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6Mi8kaGiNbGtIrZR .rough-node .label text,#mermaid-svg-6Mi8kaGiNbGtIrZR .node .label text,#mermaid-svg-6Mi8kaGiNbGtIrZR .image-shape .label,#mermaid-svg-6Mi8kaGiNbGtIrZR .icon-shape .label{text-anchor:middle;}#mermaid-svg-6Mi8kaGiNbGtIrZR .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6Mi8kaGiNbGtIrZR .rough-node .label,#mermaid-svg-6Mi8kaGiNbGtIrZR .node .label,#mermaid-svg-6Mi8kaGiNbGtIrZR .image-shape .label,#mermaid-svg-6Mi8kaGiNbGtIrZR .icon-shape .label{text-align:center;}#mermaid-svg-6Mi8kaGiNbGtIrZR .node.clickable{cursor:pointer;}#mermaid-svg-6Mi8kaGiNbGtIrZR .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6Mi8kaGiNbGtIrZR .arrowheadPath{fill:#333333;}#mermaid-svg-6Mi8kaGiNbGtIrZR .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6Mi8kaGiNbGtIrZR .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6Mi8kaGiNbGtIrZR .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6Mi8kaGiNbGtIrZR .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6Mi8kaGiNbGtIrZR .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6Mi8kaGiNbGtIrZR .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6Mi8kaGiNbGtIrZR .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6Mi8kaGiNbGtIrZR .cluster text{fill:#333;}#mermaid-svg-6Mi8kaGiNbGtIrZR .cluster span{color:#333;}#mermaid-svg-6Mi8kaGiNbGtIrZR div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6Mi8kaGiNbGtIrZR .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6Mi8kaGiNbGtIrZR rect.text{fill:none;stroke-width:0;}#mermaid-svg-6Mi8kaGiNbGtIrZR .icon-shape,#mermaid-svg-6Mi8kaGiNbGtIrZR .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6Mi8kaGiNbGtIrZR .icon-shape p,#mermaid-svg-6Mi8kaGiNbGtIrZR .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6Mi8kaGiNbGtIrZR .icon-shape .label rect,#mermaid-svg-6Mi8kaGiNbGtIrZR .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6Mi8kaGiNbGtIrZR .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6Mi8kaGiNbGtIrZR .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6Mi8kaGiNbGtIrZR :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Python OOP 模型
Java OOP 模型
差异
差异
差异
差异
强封装
private/protected/public
单继承 + 多接口
基于类型的多态
向上转型/向下转型
显式异常声明
throws/checked exception
约定式封装
_protected / __name_mangling
多继承 + MRO
鸭子类型多态
'If it walks like a duck...'
无 checked exception
运行时自由传播
一、核心知识
1.1 Python OOP 的设计哲学
Python OOP 遵循三条核心原则:
-
"我们都是成年人"(We're all consenting adults) ------不强加访问控制,靠命名约定和信任。
_name表示"请勿直接访问",但不会阻止你。 -
"简单胜于复杂" ------没有
interface、abstract关键字、final等 Java 概念。多态靠鸭子类型,抽象靠 ABC 模块。 -
"一切皆对象" ------类本身也是对象(
type的实例)。这意味着你可以在运行时动态修改类、添加属性。这在元编程和框架开发中极其强大。
1.2 异常处理哲学
Python 对待异常的态度与 Java 截然不同:
- Java:checked exception 强制调用者处理或声明
- Python:所有异常都是 unchecked------调用者自行决定是否捕获
- Python 社区更倾向于 EAFP(Easier to Ask for Forgiveness than Permission)------先尝试操作,失败再处理异常
python
# EAFP 风格:先试,失败了再说
try:
value = config["key"]
except KeyError:
value = "default"
# LBYL 风格(Look Before You Leap)------不推荐
if "key" in config:
value = config["key"]
else:
value = "default"
二、详细知识讲解
2.1 类与对象
定义一个类
python
class Agent:
"""AI Agent 基类"""
# 类属性------所有实例共享
default_model = "gpt-4o"
agent_count = 0
def __init__(self, name: str, model: str | None = None):
"""构造方法------第一个参数必须是 self"""
self.name = name # 实例属性
self.model = model or self.default_model
self.status = "initialized"
# 修改类属性
Agent.agent_count += 1
def activate(self):
"""实例方法"""
self.status = "active"
return f"{self.name} is now active"
@classmethod
def get_count(cls):
"""类方法------第一个参数是类本身"""
return cls.agent_count
@staticmethod
def validate_name(name: str) -> bool:
"""静态方法------不需要访问实例或类"""
return bool(name) and len(name) <= 50
def __str__(self):
"""字符串表示------给用户看的"""
return f"Agent({self.name}, model={self.model})"
def __repr__(self):
"""调试表示------给开发者看的"""
return f"Agent(name={self.name!r}, model={self.model!r})"
self------显式的 this
Python 必须显式声明 self 参数。这看起来啰嗦,但有深刻的设计意图:
python
class Agent:
def greet(self, greeting="Hello"):
return f"{greeting}, I'm {self.name}"
# 以下两种调用方式等价:
a = Agent()
a.greet("Hi") # 通常写法
Agent.greet(a, "Hi") # 底层实际调用------self 就是 a
为什么 Python 要显式 self? 禅意:"显式胜于隐式"。这让方法内部的变量访问一目了然------name 是局部变量,self.name 是实例属性。同时与函数统一(def 定义就是函数,self 让它区分于普通函数)。
类属性 vs 实例属性
python
class Agent:
api_version = "v1" # 类属性------所有实例共享
def __init__(self, name):
self.name = name # 实例属性------每个实例独有
self.api_version = "v2" # 实例属性------遮蔽了类属性!
a1 = Agent("alpha")
a2 = Agent("beta")
print(a1.name) # "alpha" ------ 实例属性
print(a1.api_version) # "v2" ------ 实例遮蔽了类属性的值
print(Agent.api_version) # "v1" ------ 类属性不变
print(a2.api_version) # "v2" ------ 每个实例都创建了独立的 api_version
注意 :类属性修改和实例属性的同名赋值是不同的。
Agent.x = 5修改类属性,self.x = 5创建实例属性。
2.2 @property------Python 版 getter/setter
作为 Java 开发者,你一定写过无数的 getter/setter。Python 对此嗤之以鼻------直接访问属性是默认方式,@property 只在需要计算或验证时使用:
python
class Agent:
def __init__(self, name: str, temperature: float = 0.7):
self._name = name # _ 前缀 = "请勿直接访问"
self._temperature = temperature
@property
def name(self) -> str:
"""Getter------agent.name 而不是 agent.getName()"""
return self._name
@name.setter
def name(self, value: str):
"""Setter------agent.name = "new" 触发"""
if not value:
raise ValueError("Name cannot be empty")
self._name = value
@property
def temperature(self) -> float:
return self._temperature
@temperature.setter
def temperature(self, value: float):
if not 0 <= value <= 2:
raise ValueError(f"Temperature must be 0-2, got {value}")
self._temperature = value
@property
def model_display_name(self) -> str:
"""只读计算属性"""
return f"{self._name} (temp={self._temperature})"
# 使用------像访问普通属性一样
agent = Agent("assistant")
print(agent.name) # "assistant" ------ 调用了 getter
agent.name = "helper" # 调用了 setter(包含验证)
# agent.name = "" # ValueError!
print(agent.model_display_name) # "helper (temp=0.7)"
对比 Java Lombok :
@property≈@Getter+@Setter组合,但 Python 语法更自然------调用侧看起来像直接访问属性,而不是调用getXxx()。
2.3 继承
单继承
python
class Agent:
def __init__(self, name, model="gpt-4o"):
self.name = name
self.model = model
def process(self, message: str) -> str:
raise NotImplementedError # 子类必须实现
class ChatAgent(Agent):
"""聊天 Agent"""
def process(self, message: str) -> str:
return f"[ChatAgent {self.name}] Processing: {message}"
class CodeAgent(Agent):
"""代码 Agent"""
def __init__(self, name, model="gpt-4o", language="python"):
super().__init__(name, model) # 调用父类构造
self.language = language
def process(self, message: str) -> str:
return f"[CodeAgent {self.name}] {self.language}: {message}"
# 使用
chat = ChatAgent("assistant")
print(chat.process("Hello")) # [ChatAgent assistant] Processing: Hello
code = CodeAgent("reviewer", language="python")
print(code.process("Review this")) # [CodeAgent reviewer] python: Review this
多继承与 MRO
Python 支持多继承------这是 Java 不具备的能力。多个父类的同名方法解析顺序由 MRO(Method Resolution Order)决定:
python
class Loggable:
"""可记录日志的混入类"""
def log(self, message):
print(f"[LOG] {message}")
class Serializable:
"""可序列化的混入类"""
def to_dict(self):
return {"name": getattr(self, "name", "unknown")}
def log(self, message): # 与 Loggable 有同名方法
print(f"[SER] {message}")
# 多继承
class SmartAgent(Agent, Loggable, Serializable):
"""同时继承三个类"""
def process(self, message):
self.log(f"Processing: {message}") # MRO 决定调用谁
return f"Done: {message}"
# 查看 MRO
print(SmartAgent.__mro__)
# (SmartAgent, Agent, Loggable, Serializable, object)
# 按此顺序查找方法------先找 SmartAgent → Agent → Loggable → Serializable → object
MRO 规则(C3 线性化算法):
- 子类优先于父类
- 父类按声明顺序排列
- 所有父类必须在其祖先之前
混入类(Mixin) :Python 社区常用多继承实现 Mixin------提供特定功能的小型类(如
Loggable、Serializable),通过组合 Mixin 构建功能丰富的类。类似 Java 8+ 接口的 default method,但可以包含状态。
2.4 鸭子类型------Python 多态的真谛
"如果它走起来像鸭子,叫起来像鸭子,那它就是鸭子。"
python
# 无需继承同一父类或实现同一接口------只要实现了 process() 方法即可
class ChatAgent:
def process(self, message: str) -> str:
return f"Chat: {message}"
class CodeAgent:
def process(self, message: str) -> str:
return f"Code: {message}"
class CustomProcessor:
"""自定义处理器------与 Agent 无任何继承关系"""
def process(self, message: str) -> str:
return f"Custom: {message.upper()}"
# run_pipeline 不关心参数类型,只关心有没有 process() 方法
def run_pipeline(processor, messages):
"""管道处理------接受任何有 process() 方法的对象"""
return [processor.process(m) for m in messages]
# 全部可以传入!
for agent in [ChatAgent(), CodeAgent(), CustomProcessor()]:
print(run_pipeline(agent, ["hello", "world"]))
这解释了为什么 LangChain 中可以不修改任何调用代码就替换模型实现------只要新类实现了相同的接口(方法签名),鸭子类型自动适配。Java 中需要抽取 interface Agent { String process(String msg); }------Python 中这步是可选的。
2.5 魔术方法(Dunder Methods)
以双下划线开头和结尾的方法被称为魔术方法。它们让自定义类的行为像内置类型:
python
class Agent:
def __init__(self, name: str, score: float = 0.0):
self.name = name
self.score = score
self.tools: list[str] = []
# ===== 字符串表示 =====
def __str__(self) -> str:
"""print(obj) / str(obj) 时调用------面向用户"""
return f"Agent({self.name})"
def __repr__(self) -> str:
"""交互环境 / repr(obj) 时调用------面向开发者"""
return f"Agent(name={self.name!r}, score={self.score})"
# ===== 比较 =====
def __eq__(self, other) -> bool:
"""== 操作符"""
if not isinstance(other, Agent):
return NotImplemented
return self.name == other.name
def __lt__(self, other) -> bool:
"""< 操作符------只需实现这一个,sorted() 就可用"""
if not isinstance(other, Agent):
return NotImplemented
return self.score < other.score
def __hash__(self) -> int:
"""hash() ------ 用于 dict 的 key 和 set 成员"""
return hash(self.name)
# ===== 容器行为 =====
def __len__(self) -> int:
"""len(obj) ------ 返回工具数量"""
return len(self.tools)
def __contains__(self, item) -> bool:
"""in 操作符 ------ 'search' in agent"""
return item in self.tools
def __getitem__(self, index):
"""agent[0] ------ 索引访问工具"""
return self.tools[index]
# ===== 可调用 =====
def __call__(self, message: str) -> str:
"""agent('hello') ------ 把对象当函数调用"""
return f"{self.name} received: {message}"
# 使用
a1 = Agent("alpha", score=85)
a2 = Agent("beta", score=92)
a3 = Agent("alpha", score=100) # 同名
print(a1 == a3) # True ------ __eq__ 按 name 比较
print(a1 < a2) # True ------ __lt__ 按 score 比较
# 排序
agents = [a2, a1, a3]
agents.sort() # 使用 __lt__
print([a.name for a in agents]) # ['alpha', 'alpha', 'beta']
# 作为字典 key
registry = {a1: "active", a2: "idle"}
# 可调用
a1.tools = ["search", "code"]
print(len(a1)) # 2 ------ __len__
print("search" in a1) # True ------ __contains__
print(a1("hello")) # "alpha received: hello" ------ __call__
常用魔术方法速查
| 方法 | 触发条件 | Agent 场景 |
|---|---|---|
__str__ |
str(obj), print() |
日志输出 |
__repr__ |
repr(obj), 交互环境 |
调试输出 |
__eq__ |
== |
Agent 比较 |
__lt__ |
<, sorted() |
按优先级排序 |
__hash__ |
hash(), dict key |
缓存键值 |
__len__ |
len(obj) |
工具数量 |
__contains__ |
in |
工具检查 |
__call__ |
obj() |
LangChain Runnable |
__enter__/__exit__ |
with |
资源管理 |
__iter__/__next__ |
for |
遍历消息流 |
2.6 dataclass------告别样板代码
python
from dataclasses import dataclass, field
from typing import Any
# 传统写法(大量样板代码)
class AgentOld:
def __init__(self, name, model="gpt-4o", temperature=0.7, tools=None):
self.name = name
self.model = model
self.temperature = temperature
self.tools = tools if tools is not None else []
def __repr__(self):
return f"Agent(name={self.name!r}, model={self.model!r})"
def __eq__(self, other):
if not isinstance(other, AgentOld):
return False
return self.name == other.name
# dataclass 写法------等价于上面全部
@dataclass
class Agent:
name: str
model: str = "gpt-4o"
temperature: float = 0.7
tools: list[str] = field(default_factory=list)
# __init__、__repr__、__eq__ 自动生成!
# 高级选项
@dataclass(frozen=True) # 不可变(类似 Java record)
class FrozenConfig:
name: str
max_tokens: int = 4096
@dataclass(order=True) # 自动生成 __lt__、__le__、__gt__、__ge__
class ScoredAgent:
sort_index: int = field(init=False, repr=False)
name: str = ""
score: float = 0.0
def __post_init__(self):
"""dataclass 的初始化后钩子"""
self.sort_index = int(self.score * 100)
dataclass vs Java Lombok :
@dataclass自动生成__init__、__repr__、__eq__,等价于 Lombok 的@Data。frozen=True的 dataclass 类似 Java 14+ 的record。
2.7 枚举(Enum)
python
from enum import Enum, auto, StrEnum
class AgentStatus(Enum):
"""Agent 状态枚举"""
INITIALIZED = "initialized"
RUNNING = "running"
IDLE = "idle"
ERROR = "error"
STOPPED = "stopped"
class ModelProvider(StrEnum): # Python 3.11+,自动支持字符串比较
"""模型提供商"""
OPENAI = "openai"
ANTHROPIC = "anthropic"
DEEPSEEK = "deepseek"
GOOGLE = "google"
# 使用
status = AgentStatus.RUNNING
print(status) # AgentStatus.RUNNING
print(status.value) # "running"
print(status == AgentStatus.RUNNING) # True
# 从值构造
status = AgentStatus("running") # AgentStatus.RUNNING
# 遍历
for s in AgentStatus:
print(f"{s.name} = {s.value}")
# StrEnum 的好处------直接字符串比较
provider = ModelProvider.OPENAI
print(provider == "openai") # True!
2.8 抽象基类(ABC)
Python 没有 interface 或 abstract class 关键字,但 abc 模块提供了类似能力:
python
from abc import ABC, abstractmethod
class BaseAgent(ABC):
"""Agent 抽象基类------定义了 Agent 必须实现的接口"""
@abstractmethod
def process(self, message: str) -> str:
"""处理消息------子类必须实现"""
...
@abstractmethod
def get_capabilities(self) -> list[str]:
"""返回能力列表"""
...
# 非抽象方法------提供默认实现(类似 Java 接口的 default 方法)
def greet(self) -> str:
return f"Hello, I'm a {self.__class__.__name__}"
# agent = BaseAgent() # TypeError! 不能实例化抽象类
class ChatAgent(BaseAgent):
def process(self, message: str) -> str:
return f"Chat: {message}"
def get_capabilities(self) -> list[str]:
return ["chat", "summarize"]
ABC vs Java 对照:ABC ≈ Java 的 abstract class + interface(合二为一)。Python 中的 ABC 检查在实例化时触发(而不是编译时),只有全部抽象方法都实现后才能创建实例。
2.9 异常处理
try / except / else / finally
python
# 完整结构
try:
# 可能抛出异常的代码
result = risky_operation()
except ValueError as e:
# 捕获特定异常
print(f"Value error: {e}")
except (TypeError, KeyError) as e:
# 同时捕获多种异常
print(f"Type or Key error: {e}")
except Exception as e:
# 捕获所有异常(不建议宽泛捕获)
print(f"Unexpected error: {e}")
else:
# try 块成功执行(无异常)时运行
print(f"Success! Result: {result}")
finally:
# 无论是否有异常,始终运行
cleanup_resources()
else 子句的价值:将"依赖 try 成功结果的代码"和"可能抛异常的代码"分离:
python
# ❌ 不加 else------result 的作用域模糊
try:
config = load_config() # 可能抛异常
validate_config(config) # 依赖 load_config 成功,不要放这里!
except FileNotFoundError:
print("Config not found")
# ✅ 加 else------语义清晰
try:
config = load_config() # 可能抛异常
except FileNotFoundError:
print("Config not found")
else:
validate_config(config) # 仅在 load 成功时执行
自定义异常
python
class AgentError(Exception):
"""Agent 异常基类"""
def __init__(self, message: str, agent_name: str | None = None):
super().__init__(message)
self.agent_name = agent_name
self.message = message
class AgentNotFoundError(AgentError):
"""Agent 未找到"""
pass
class AgentConfigurationError(AgentError):
"""Agent 配置错误"""
pass
class LLMServiceError(AgentError):
"""LLM 服务调用失败"""
def __init__(self, message: str, model: str, status_code: int | None = None):
super().__init__(message)
self.model = model
self.status_code = status_code
# 使用
try:
raise AgentNotFoundError("Agent 'assistant' not registered", agent_name="assistant")
except AgentNotFoundError as e:
print(f"Error: {e.message} (agent: {e.agent_name})")
异常链------raise ... from ...
python
# 异常转换------将底层异常包装为业务异常
def load_agent_config(path):
try:
with open(path) as f:
return json.load(f)
except FileNotFoundError as e:
# 包装为业务异常,保留原始异常链
raise AgentConfigurationError(f"Config not found: {path}") from e
except json.JSONDecodeError as e:
raise AgentConfigurationError(f"Invalid JSON: {path}") from e
# 调用方可以追踪到原始异常
try:
load_agent_config("missing.json")
except AgentConfigurationError as e:
print(f"Business Error: {e}")
print(f"Caused by: {e.__cause__}") # 原始 FileNotFoundError
2.10 上下文管理器
手动实现
python
class AgentSession:
"""Agent 会话------自动管理资源生命周期"""
def __init__(self, agent_name: str):
self.agent_name = agent_name
def __enter__(self):
"""进入 with 块时调用"""
print(f"Starting session for {self.agent_name}")
self.start_time = time.time()
return self # 返回值赋给 as 变量
def __exit__(self, exc_type, exc_val, exc_tb):
"""退出 with 块时调用(即使发生异常也会调用)"""
elapsed = time.time() - self.start_time
print(f"Ending session for {self.agent_name} (took {elapsed:.2f}s)")
# 返回 True 会抑制异常(通常不这样做)
if exc_type is AgentError:
print(f"Handled AgentError: {exc_val}")
return True # 抑制异常
return False # 不抑制,异常继续传播
def process(self, message):
return f"[{self.agent_name}] {message}"
# 使用
with AgentSession("assistant") as session:
result = session.process("Hello")
print(result)
# 输出:
# Starting session for assistant
# [assistant] Hello
# Ending session for assistant (took 0.00s)
使用 contextlib(更简洁)
python
from contextlib import contextmanager
import time
@contextmanager
def agent_session(agent_name: str):
"""用生成器实现上下文管理器------更简洁"""
print(f"Starting session for {agent_name}")
start = time.time()
try:
yield agent_name # yield 之前 = __enter__
finally:
elapsed = time.time() - start # finally = __exit__
print(f"Ending session for {agent_name} (took {elapsed:.2f}s)")
# 使用
with agent_session("assistant") as name:
print(f"Processing with {name}")
三、代码示例
示例 1:完整的 Agent 基类体系
python
#!/usr/bin/env python3
"""
企业级 AI Agent 平台------核心领域模型
演示:继承、抽象基类、dataclass、property、魔术方法
"""
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from enum import Enum, auto
from datetime import datetime
from typing import Any
# ==========================================
# 枚举定义
# ==========================================
class AgentStatus(Enum):
INITIALIZING = auto()
READY = auto()
PROCESSING = auto()
WAITING = auto()
ERROR = auto()
STOPPED = auto()
class MessageRole(Enum):
SYSTEM = "system"
USER = "user"
ASSISTANT = "assistant"
TOOL = "tool"
# ==========================================
# 领域模型
# ==========================================
@dataclass
class Message:
"""对话消息"""
role: MessageRole
content: str
timestamp: datetime = field(default_factory=datetime.now)
metadata: dict[str, Any] = field(default_factory=dict)
def to_api_dict(self) -> dict[str, Any]:
"""转为 LLM API 格式"""
return {"role": self.role.value, "content": self.content}
def __len__(self) -> int:
return len(self.content)
@dataclass
class Conversation:
"""对话会话"""
id: str
messages: list[Message] = field(default_factory=list)
created_at: datetime = field(default_factory=datetime.now)
def add_message(self, role: MessageRole, content: str) -> Message:
msg = Message(role=role, content=content)
self.messages.append(msg)
return msg
@property
def turn_count(self) -> int:
"""对话轮数"""
non_system = [m for m in self.messages if m.role != MessageRole.SYSTEM]
return len(non_system) // 2
@property
def last_message(self) -> Message | None:
return self.messages[-1] if self.messages else None
# ==========================================
# Agent 抽象基类
# ==========================================
class BaseAgent(ABC):
"""Agent 抽象基类"""
def __init__(self, name: str, model: str = "gpt-4o"):
self.name = name
self.model = model
self.status = AgentStatus.INITIALIZING
self._conversation: Conversation | None = None
@abstractmethod
def process(self, message: str) -> str:
"""处理消息------子类必须实现"""
...
@abstractmethod
def get_capabilities(self) -> list[str]:
"""返回能力列表"""
...
def activate(self) -> None:
"""激活 Agent"""
self.status = AgentStatus.READY
self._conversation = Conversation(id=f"{self.name}_{datetime.now().timestamp()}")
def deactivate(self) -> None:
"""停用 Agent"""
self.status = AgentStatus.STOPPED
@property
def is_active(self) -> bool:
return self.status in (AgentStatus.READY, AgentStatus.PROCESSING, AgentStatus.WAITING)
def __str__(self) -> str:
return f"{self.__class__.__name__}({self.name}, {self.status.name})"
def __repr__(self) -> str:
return f"{self.__class__.__name__}(name={self.name!r}, model={self.model!r})"
# ==========================================
# 具体 Agent 实现
# ==========================================
class ChatAgent(BaseAgent):
"""聊天 Agent"""
def __init__(self, name: str, model: str = "gpt-4o", system_prompt: str = ""):
super().__init__(name, model)
self.system_prompt = system_prompt
def process(self, message: str) -> str:
self.status = AgentStatus.PROCESSING
response = f"[ChatAgent {self.name}] {message}" # 实际调用 LLM
self.status = AgentStatus.READY
return response
def get_capabilities(self) -> list[str]:
return ["chat", "summarize", "translate"]
class CodeAgent(BaseAgent):
"""代码 Agent"""
def __init__(self, name: str, language: str = "python", model: str = "gpt-4o"):
super().__init__(name, model)
self.language = language
def process(self, message: str) -> str:
self.status = AgentStatus.PROCESSING
response = f"[CodeAgent {self.name}] {self.language}: {message}"
self.status = AgentStatus.READY
return response
def get_capabilities(self) -> list[str]:
return ["code_review", "refactor", "generate_tests", f"write_{self.language}"]
if __name__ == "__main__":
# 创建 Agent
chat = ChatAgent("assistant", system_prompt="You are helpful.")
chat.activate()
print(chat) # ChatAgent(assistant, READY)
print(f"Capabilities: {chat.get_capabilities()}")
print(chat.process("Hello!"))
# 对话管理
conv = Conversation(id="test-1")
conv.add_message(MessageRole.USER, "What is Python?")
conv.add_message(MessageRole.ASSISTANT, "Python is...")
print(f"Turns: {conv.turn_count}")
print(f"Last message: {conv.last_message.content}")
四、实战案例
案例 1:全局异常体系
python
#!/usr/bin/env python3
"""
企业级 AI Agent 平台------全局异常体系
设计为三层:基础异常 → 业务异常 → 具体异常
"""
from typing import Any
# ==========================================
# 第一层:基础异常
# ==========================================
class AgentPlatformError(Exception):
"""平台所有异常的基类"""
def __init__(self, message: str, *, code: str | None = None, details: dict[str, Any] | None = None):
super().__init__(message)
self.message = message
self.code = code or "AGENT_ERROR"
self.details = details or {}
def to_dict(self) -> dict[str, Any]:
return {
"error": self.code,
"message": self.message,
"details": self.details,
}
# ==========================================
# 第二层:业务域异常
# ==========================================
class AgentNotFoundError(AgentPlatformError):
"""Agent 不存在"""
def __init__(self, agent_name: str):
super().__init__(
f"Agent '{agent_name}' not found",
code="AGENT_NOT_FOUND",
details={"agent_name": agent_name},
)
class AgentConfigurationError(AgentPlatformError):
"""Agent 配置错误"""
def __init__(self, message: str, **details):
super().__init__(message, code="AGENT_CONFIG_ERROR", details=details)
class ConversationError(AgentPlatformError):
"""对话相关异常"""
def __init__(self, message: str, conversation_id: str | None = None):
super().__init__(
message,
code="CONVERSATION_ERROR",
details={"conversation_id": conversation_id},
)
# ==========================================
# 第二层:外部集成异常
# ==========================================
class ExternalServiceError(AgentPlatformError):
"""外部服务调用异常"""
def __init__(self, service: str, message: str, status_code: int | None = None):
super().__init__(
f"{service}: {message}",
code="EXTERNAL_SERVICE_ERROR",
details={"service": service, "status_code": status_code},
)
class LLMServiceError(ExternalServiceError):
"""LLM 服务异常"""
def __init__(self, model: str, message: str, status_code: int | None = None):
super().__init__(service=f"LLM({model})", message=message, status_code=status_code)
class TokenLimitExceededError(LLMServiceError):
"""Token 超限"""
def __init__(self, model: str, current_tokens: int, max_tokens: int):
super().__init__(model, f"Token limit exceeded: {current_tokens}/{max_tokens}")
self.details["current_tokens"] = current_tokens
self.details["max_tokens"] = max_tokens
# ==========================================
# 使用示例
# ==========================================
def load_agent(name: str):
"""模拟加载 Agent------演示异常处理"""
if name == "evil":
raise AgentNotFoundError(name)
if name == "broken":
raise AgentConfigurationError(
"Invalid temperature",
temperature=999,
valid_range="0-2",
)
return {"name": name, "status": "loaded"}
def call_llm(model: str, prompt: str):
"""模拟 LLM 调用"""
if len(prompt) > 1000:
raise TokenLimitExceededError(model, len(prompt), 1000)
if "timeout" in prompt.lower():
raise LLMServiceError(model, "Request timeout", status_code=504)
return f"Response from {model}"
if __name__ == "__main__":
# 演示异常捕获与转换
for agent_name in ["assistant", "evil", "broken"]:
try:
load_agent(agent_name)
print(f"✅ {agent_name}: loaded")
except AgentNotFoundError as e:
print(f"❌ {e.to_dict()}")
except AgentConfigurationError as e:
print(f"⚠️ {e.to_dict()}")
# 演示 LLM 异常
for prompt in ["Hello", "Error timeout", "A" * 2000]:
try:
call_llm("gpt-4o", prompt)
except TokenLimitExceededError as e:
print(f"⚠️ {e.to_dict()}")
except LLMServiceError as e:
print(f"❌ {e.to_dict()}")
案例 2:可配置的 Agent 工厂
python
#!/usr/bin/env python3
"""
Agent 工厂------使用 dataclass + 注册模式
"""
from dataclasses import dataclass, field
from typing import Callable
@dataclass(frozen=True)
class AgentSpec:
"""Agent 规格定义"""
name: str
agent_type: str
model: str = "gpt-4o"
temperature: float = 0.7
system_prompt: str = ""
tools: list[str] = field(default_factory=list)
metadata: dict = field(default_factory=dict)
class AgentRegistry:
"""Agent 注册中心"""
def __init__(self):
self._creators: dict[str, Callable] = {}
self._specs: dict[str, AgentSpec] = {}
def register(self, agent_type: str, creator: Callable):
"""注册 Agent 创建器"""
self._creators[agent_type] = creator
def create(self, spec: AgentSpec):
"""根据 spec 创建 Agent 实例"""
if spec.agent_type not in self._creators:
raise ValueError(f"Unknown agent type: {spec.agent_type}")
self._specs[spec.name] = spec
return self._creators[spec.agent_type](spec)
def list_agents(self) -> list[str]:
return list(self._specs.keys())
if __name__ == "__main__":
registry = AgentRegistry()
# 注册创建器(鸭子类型------任何接受 AgentSpec 的可调用对象)
registry.register("chat", lambda spec: f"ChatAgent({spec.name}, {spec.model})")
registry.register("code", lambda spec: f"CodeAgent({spec.name}, {spec.model})")
spec = AgentSpec(name="assistant", agent_type="chat", system_prompt="Be helpful")
agent = registry.create(spec)
print(agent) # ChatAgent(assistant, gpt-4o)
五、Java 开发者对照学习
5.1 核心差异表
| 概念 | Java | Python |
|---|---|---|
| 访问控制 | private/protected/public |
_protected / __name_mangling(约定) |
| 构造器 | 类名作为方法名 | __init__(self) |
| this | 隐式关键字 | 显式 self 参数 |
| 继承 | 单继承 + 多接口 | 多继承 + MRO |
| 多态 | 基于继承层级和接口 | 鸭子类型(duck typing) |
| 抽象 | abstract class / interface |
ABC + @abstractmethod |
| 接口 | interface 关键字 |
ABC 或鸭子类型 |
| final | 阻止继承/覆写 | 无(约定或元类模拟) |
| getter/setter | 标配 | 先直访,需要时加 @property |
| 异常 | checked + unchecked | 全 unchecked |
| 资源管理 | try-with-resources | with 语句 + 上下文管理器 |
5.2 Java vs Python 代码对照
java
// Java
public interface Processor {
String process(String input);
List<String> getCapabilities();
}
public class ChatAgent implements Processor {
private final String name;
private String status = "initialized";
public ChatAgent(String name) {
this.name = name;
}
public String getName() { return name; }
public String getStatus() { return status; }
@Override
public String process(String input) {
this.status = "processing";
// ...
this.status = "ready";
return "Response";
}
@Override
public List<String> getCapabilities() {
return List.of("chat");
}
}
python
# Python------同样的功能,1/3 的代码
class ChatAgent:
def __init__(self, name):
self.name = name
self.status = "initialized"
def process(self, input):
self.status = "processing"
# ...
self.status = "ready"
return "Response"
def get_capabilities(self):
return ["chat"]
def run_pipeline(processor, messages):
# 鸭子类型------不要求 processor 实现特定接口
return [processor.process(m) for m in messages]
5.3 容易踩的坑
坑 1:__init__ 不是构造器
python
# __init__ 是初始化器,不是构造器!
# 真正的构造器是 __new__(极少直接使用)
class Agent:
def __new__(cls, *args, **kwargs):
# 真正的构造------返回实例
instance = super().__new__(cls)
return instance
def __init__(self, name): # 初始化------设置属性
self.name = name
坑 2:类属性被实例属性遮蔽
python
class Agent:
registry = [] # 类属性------以为所有实例共享同一个列表
def register(self, name):
self.registry.append(name) # 追加到类属性的列表(因为是可变对象)
a1 = Agent()
a2 = Agent()
a1.register("alpha")
print(a2.registry) # ["alpha"] ------ OK,共享
# 但如果这样:
a1.registry = ["alpha"] # 创建了实例属性!不再是类属性
print(a2.registry) # [] ------ 类属性没变
坑 3:多继承的 MRO 陷阱
python
class A:
def method(self): return "A"
class B(A):
def method(self): return "B"
class C(A):
def method(self): return "C"
class D(B, C):
pass
print(D().method()) # "B" ------ MRO: D → B → C → A
# 如果写成 class D(C, B): method() 返回 "C"
六、企业最佳实践
6.1 异常体系设计
python
# ✅ 企业级三层异常体系
# 第一层:应用根异常(所有异常基类)
class AppError(Exception): ...
# 第二层:业务域异常(按模块/域划分)
class DomainError(AppError): ...
class InfraError(AppError): ...
# 第三层:具体异常
class AgentNotFoundError(DomainError): ...
class DatabaseConnectionError(InfraError): ...
# ✅ 异常应包含足够的上下文信息
# ✅ 使用 raise ... from ... 保留异常链
# ✅ API 层统一捕获 AppError,转为 HTTP 响应
6.2 OOP 设计指南
| 场景 | 推荐方案 | 说明 |
|---|---|---|
| 数据对象(DTO) | @dataclass |
自动生成 __init__/__repr__/__eq__ |
| 不可变配置 | @dataclass(frozen=True) |
类似 Java record |
| 常量枚举 | Enum / StrEnum |
比字符串字面量更安全 |
| 接口定义 | ABC + 鸭子类型 | ABC 加运行时检查,宽松场景用鸭子类型 |
| 访问控制 | _ 前缀约定 |
不试图强制 private |
| 资源管理 | with + 上下文管理器 |
绝对不用 try-finally |
6.3 isinstance vs type
python
# ✅ 用 isinstance(支持继承)
if isinstance(obj, BaseAgent):
obj.process("hi")
# ❌ 通常不用 type(不考虑继承)
if type(obj) is BaseAgent: # 只匹配 BaseAgent 本身,不匹配子类
pass
# ✅ isinstance 支持元组------检查多种类型
if isinstance(obj, (Agent, ChatAgent)):
pass
# ⚠️ 但鸭子类型更 Pythonic
try:
obj.process("hi") # 不检查类型,直接调用
except AttributeError:
print("Object doesn't support process()")
七、常见错误
| 错误 | 原因 | 解决 |
|---|---|---|
self 忘记写 |
实例方法第一个参数必须是 self |
检查方法签名 |
| 类属性被意外遮蔽 | self.attr = x 创建实例属性 |
修改类属性用 ClassName.attr |
| MRO 顺序困惑 | 多继承解析不如预期 | 用 ClassName.__mro__ 查看 |
except Exception 太宽泛 |
吞掉了 KeyboardInterrupt 等 |
捕获具体异常类型 |
__init__ 返回值 |
不应该返回除 None 外的值 | __init__ 是初始化,不是构造 |
| 异常时资源泄漏 | try 中打开资源,except 中忘记关闭 |
用 with 语句 |
八、本章总结
| 能力 | 核心要点 | Agent 场景 |
|---|---|---|
| 类定义 | class/__init__/self |
领域模型 |
| @property | 属性验证、计算属性 | 配置校验、动态属性 |
| 多继承 | MRO、Mixin 模式 | 混入日志、序列化能力 |
| 鸭子类型 | 不依赖类型层级 | LangChain 的模型替换 |
| 魔术方法 | __str__/__eq__/__call__ |
打印、比较、作为函数调用 |
| dataclass | 减少 80% 样板代码 | 消息模型、配置类 |
| 异常体系 | 三层设计、异常链 | 全局错误处理 |
| 上下文管理器 | with/__enter__/contextlib |
资源生命周期管理 |
九、面试题
基础题
Q1:Python 中 __init__ 和 __new__ 的区别?
参考答案
__new__ 是真正的构造方法------创建并返回实例对象,是类方法(第一个参数是 cls),在 __init__ 之前调用。__init__ 是初始化方法------设置实例属性,不返回实例。日常开发 99% 只需 __init__,__new__ 主要用于创建不可变类型的子类或实现单例模式。
Q2:什么是鸭子类型?与 Java 基于接口的多态有何区别?
参考答案
鸭子类型:不检查对象的具体类型,只检查是否有所需的方法/属性。"如果它走起来像鸭子,叫起来像鸭子,那它就是鸭子"。与 Java 的区别:Java 必须显式声明 implements SomeInterface,编译时检查;Python 运行时检查,更灵活但安全性靠约定和测试保证。在 Agent 场景中,鸭子类型让你可以无缝替换 LLM 提供者、工具实现,无需修改接口定义。
原理题
Q3:Python 多继承的 MRO 如何工作?为什么要用 C3 线性化?
参考答案
MRO(Method Resolution Order)决定了多继承时方法查找的顺序。Python 采用 C3 线性化算法,保证:1)子类优先于父类;2)父类按声明顺序排列;3)所有父类在其祖先之前。可以用 ClassName.__mro__ 或 ClassName.mro() 查看。C3 确保了在复杂的继承图中不会出现歧义------如果无法构建满足所有约束的线性顺序,Python 会在类定义时直接抛出 TypeError。
Q4:raise X from Y 和直接 raise X 在异常链上有何不同?
参考答案
raise X from Y 显式设置了 X.__cause__ = Y,表明 Y 是导致 X 的原因。raise X(在 except 块中)会隐式设置 X.__context__ = Y(原始异常),但不设置 __cause__。区别在于 __cause__ 表示"这个异常是由那个异常直接导致的",__context__ 表示"在处理那个异常时发生了这个异常"。企业代码中推荐显式使用 from 来明确异常转换意图。
开放题
Q5:设计一个"可插拔工具系统",Agent 可以动态注册和调用工具。考虑类型安全、错误处理、工具发现机制。
参考思路
核心设计:
- 抽象基类
BaseTool定义name、description、run(**kwargs)接口 @dataclass的ToolSpec承载工具的元数据(参数 schema、返回值类型)ToolRegistry使用字典存储工具名到实例的映射- 运行时用
isinstance校验注册的工具类型(防御性) - 调用时用
try/except包裹工具执行,统一转为ToolExecutionError - 工具发现:使用
__subclasses__()自动发现BaseTool的子类,或用装饰器@register_tool显式注册
这与 LangChain 的 @tool 装饰器思路一致,但在企业环境中需要加上更严格的参数校验和审计日志。
十、练习题
选择题
-
Python 类中实例方法的第一个参数约定叫什么?
- A)
thisB)selfC)meD)instance
- A)
-
以下哪个可以阻止
dataclass实例的属性被修改?- A)
@dataclass(final=True)B)@dataclass(frozen=True)C)@dataclass(immutable=True)
- A)
-
isinstance(obj, (A, B))的含义是?- A) obj 是 A 且是 B 的实例 B) obj 是 A 或 B 的实例 C) 语法错误
-
以下哪个不是 Python 上下文管理器的实现方式?
- A)
__enter__/__exit__B)@contextmanagerC)@withmanager
- A)
-
raise ValueError("bad") from original_error后,如何获取 original_error?- A)
e.__cause__B)e.originalC)e.source
- A)
填空题
-
Python 使用
____前缀来约定"受保护的"属性,使用____前缀触发名称改写(name mangling)。 -
@classmethod的第一个参数是____,@staticmethod没有特殊的第一个参数。 -
上下文管理器的
__exit__方法接收三个参数:____、____、____。 -
通过
____模块的____装饰器,可以用生成器函数创建上下文管理器。 -
ABC是____的缩写,用于定义抽象基类。
代码阅读题
-
写出输出:
pythonclass A: x = 1 class B(A): pass a = A(); b = B() b.x = 2; A.x = 3 print(a.x, b.x, A.x) -
写出输出:
pythonclass Agent: def __enter__(self): print("enter"); return self def __exit__(self, *a): print("exit"); return False with Agent() as a: print("body")
编程题
-
用
@dataclass定义ToolResult类,包含success: bool、data: Any、error: str | None。 -
实现
RetryableAgent类,process方法失败时自动重试(装饰器+异常处理)。 -
用
ABC定义LLMProvider抽象基类,然后实现OpenAIProvider和AnthropicProvider。
十一、今日作业
基础
- 将第 3 章的消息模型用
@dataclass重写 - 为 Agent 平台定义三层异常体系并编写使用示例
- 用上下文管理器实现一个计时器------进入时开始计时,退出时打印耗时
中等
- 实现
LruCache类,支持__getitem__/__setitem__/__len__/__contains__ - 用多继承 Mixin 模式实现 "日志记录 + JSON 序列化" 的可组合功能
- 为
AgentRegistry添加类型校验------注册时检查是否实现了process方法
挑战
- 研究
__slots__机制,对比它与默认__dict__的内存和性能差异 - 实现一个简单的依赖注入容器:支持按类型注册和解析,基于鸭子类型自动装配
十二、预习下一章
第 5 章:模块、包与 Python 工程化基础
下一章将学习:模块与包机制、__init__.py、import 原理、pyproject.toml 完整配置、uv 项目管理、项目分层架构、ruff + pre-commit 代码规范。这些知识将用于将目前零散的代码重构为企业级分层项目结构。本章是 Python 基础阶段的最后一章,也是进入工程化阶段的门槛。
建议预习思考:pyproject.toml 与 Maven 的 pom.xml 有什么异同?Python 的 import 机制与 Java 的 import 有何本质区别?