学习第 4 天:面向对象与异常处理

学习第 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 遵循三条核心原则:

  1. "我们都是成年人"(We're all consenting adults) ------不强加访问控制,靠命名约定和信任。_name 表示"请勿直接访问",但不会阻止你。

  2. "简单胜于复杂" ------没有 interfaceabstract 关键字、final 等 Java 概念。多态靠鸭子类型,抽象靠 ABC 模块。

  3. "一切皆对象" ------类本身也是对象(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 线性化算法):

  1. 子类优先于父类
  2. 父类按声明顺序排列
  3. 所有父类必须在其祖先之前

混入类(Mixin) :Python 社区常用多继承实现 Mixin------提供特定功能的小型类(如 LoggableSerializable),通过组合 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 的 @Datafrozen=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 没有 interfaceabstract 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 可以动态注册和调用工具。考虑类型安全、错误处理、工具发现机制。
参考思路

核心设计:

  1. 抽象基类 BaseTool 定义 namedescriptionrun(**kwargs) 接口
  2. @dataclassToolSpec 承载工具的元数据(参数 schema、返回值类型)
  3. ToolRegistry 使用字典存储工具名到实例的映射
  4. 运行时用 isinstance 校验注册的工具类型(防御性)
  5. 调用时用 try/except 包裹工具执行,统一转为 ToolExecutionError
  6. 工具发现:使用 __subclasses__() 自动发现 BaseTool 的子类,或用装饰器 @register_tool 显式注册

这与 LangChain 的 @tool 装饰器思路一致,但在企业环境中需要加上更严格的参数校验和审计日志。


十、练习题

选择题

  1. Python 类中实例方法的第一个参数约定叫什么?

    • A) this B) self C) me D) instance
  2. 以下哪个可以阻止 dataclass 实例的属性被修改?

    • A) @dataclass(final=True) B) @dataclass(frozen=True) C) @dataclass(immutable=True)
  3. isinstance(obj, (A, B)) 的含义是?

    • A) obj 是 A 且是 B 的实例 B) obj 是 A 或 B 的实例 C) 语法错误
  4. 以下哪个不是 Python 上下文管理器的实现方式?

    • A) __enter__ / __exit__ B) @contextmanager C) @withmanager
  5. raise ValueError("bad") from original_error 后,如何获取 original_error?

    • A) e.__cause__ B) e.original C) e.source

填空题

  1. Python 使用 ____ 前缀来约定"受保护的"属性,使用 ____ 前缀触发名称改写(name mangling)。

  2. @classmethod 的第一个参数是 ____@staticmethod 没有特殊的第一个参数。

  3. 上下文管理器的 __exit__ 方法接收三个参数:____________

  4. 通过 ____ 模块的 ____ 装饰器,可以用生成器函数创建上下文管理器。

  5. ABC____ 的缩写,用于定义抽象基类。

代码阅读题

  1. 写出输出:

    python 复制代码
    class A:
        x = 1
    class B(A):
        pass
    a = A(); b = B()
    b.x = 2; A.x = 3
    print(a.x, b.x, A.x)
  2. 写出输出:

    python 复制代码
    class Agent:
        def __enter__(self): print("enter"); return self
        def __exit__(self, *a): print("exit"); return False
    with Agent() as a:
        print("body")

编程题

  1. @dataclass 定义 ToolResult 类,包含 success: booldata: Anyerror: str | None

  2. 实现 RetryableAgent 类,process 方法失败时自动重试(装饰器+异常处理)。

  3. ABC 定义 LLMProvider 抽象基类,然后实现 OpenAIProviderAnthropicProvider


十一、今日作业

基础

  1. 将第 3 章的消息模型用 @dataclass 重写
  2. 为 Agent 平台定义三层异常体系并编写使用示例
  3. 用上下文管理器实现一个计时器------进入时开始计时,退出时打印耗时

中等

  1. 实现 LruCache 类,支持 __getitem__/__setitem__/__len__/__contains__
  2. 用多继承 Mixin 模式实现 "日志记录 + JSON 序列化" 的可组合功能
  3. AgentRegistry 添加类型校验------注册时检查是否实现了 process 方法

挑战

  1. 研究 __slots__ 机制,对比它与默认 __dict__ 的内存和性能差异
  2. 实现一个简单的依赖注入容器:支持按类型注册和解析,基于鸭子类型自动装配

十二、预习下一章

第 5 章:模块、包与 Python 工程化基础

下一章将学习:模块与包机制、__init__.pyimport 原理、pyproject.toml 完整配置、uv 项目管理、项目分层架构、ruff + pre-commit 代码规范。这些知识将用于将目前零散的代码重构为企业级分层项目结构。本章是 Python 基础阶段的最后一章,也是进入工程化阶段的门槛。

建议预习思考:pyproject.toml 与 Maven 的 pom.xml 有什么异同?Python 的 import 机制与 Java 的 import 有何本质区别?

相关推荐
一只小菜鸡..1 小时前
南京大学 操作系统 (JYY) 学习笔记:进程地址空间与内存“外挂”魔法
笔记·学习
BestHeaker1 小时前
试用 WorkBuddy 一段时间的个人笔记:它能做什么、积分怎么算、以及我的真实评价
个人开发·学习方法·ai编程
tju新生代魔迷1 小时前
Verilog语言学习(一)
学习
一只小菜鸡..2 小时前
南京大学 操作系统 (JYY) 学习笔记:访问操作系统的对象与“一切皆文件”
笔记·学习
yangshicong3 小时前
第19章:AI安全防护与AI安全
人工智能·python·安全·prompt·ai编程
果汁华3 小时前
Function Calling 与 Python 实战完整指南
开发语言·网络·python
c_lb72883 小时前
2026年不同基础做量化,先找AI能参与的位置
人工智能·python
chenment4 小时前
ComfyUI 自定义节点开发:从零扩展你的图像生成工作流
python·stable diffusion
IT空门:门主5 小时前
Python 基础语法学习路线图
网络·python·学习