学习第 6 天:类型注解、装饰器与高级特性
学习目标
- 掌握 Python 类型注解系统(
Optional、Union、TypeVar、Generic) - 使用 Pydantic v2 进行运行时数据验证------Python 世界的"类型安全防线"
- 深入理解装饰器原理(闭包 + 函数是一等公民),掌握装饰器实战
- 掌握生成器(
yield)和迭代器协议 - 为项目添加完整的类型标注和可复用装饰器
为什么学习本章
作为 Java 架构师,你习惯了编译器帮你检查类型。Python 的动态类型让你灵活,但也让你失去了编译时的安全网。本章的两大主题------类型注解和装饰器------恰好解决了这个问题:
- 类型注解:让你重获"类型提示"的能力,配合 mypy 实现编译时级别的类型检查
- 装饰器:Python 最优雅的 AOP(面向切面编程)机制------比 Java 的 Proxy/Interceptor 更简洁
- Pydantic:FastAPI 的基石,也是 LangChain 中广泛使用的数据验证方案
本章在知识体系中的位置
#mermaid-svg-e7Poc540IFhxOtfw{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-e7Poc540IFhxOtfw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-e7Poc540IFhxOtfw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-e7Poc540IFhxOtfw .error-icon{fill:#552222;}#mermaid-svg-e7Poc540IFhxOtfw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-e7Poc540IFhxOtfw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-e7Poc540IFhxOtfw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-e7Poc540IFhxOtfw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-e7Poc540IFhxOtfw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-e7Poc540IFhxOtfw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-e7Poc540IFhxOtfw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-e7Poc540IFhxOtfw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-e7Poc540IFhxOtfw .marker.cross{stroke:#333333;}#mermaid-svg-e7Poc540IFhxOtfw svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-e7Poc540IFhxOtfw p{margin:0;}#mermaid-svg-e7Poc540IFhxOtfw .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-e7Poc540IFhxOtfw .cluster-label text{fill:#333;}#mermaid-svg-e7Poc540IFhxOtfw .cluster-label span{color:#333;}#mermaid-svg-e7Poc540IFhxOtfw .cluster-label span p{background-color:transparent;}#mermaid-svg-e7Poc540IFhxOtfw .label text,#mermaid-svg-e7Poc540IFhxOtfw span{fill:#333;color:#333;}#mermaid-svg-e7Poc540IFhxOtfw .node rect,#mermaid-svg-e7Poc540IFhxOtfw .node circle,#mermaid-svg-e7Poc540IFhxOtfw .node ellipse,#mermaid-svg-e7Poc540IFhxOtfw .node polygon,#mermaid-svg-e7Poc540IFhxOtfw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-e7Poc540IFhxOtfw .rough-node .label text,#mermaid-svg-e7Poc540IFhxOtfw .node .label text,#mermaid-svg-e7Poc540IFhxOtfw .image-shape .label,#mermaid-svg-e7Poc540IFhxOtfw .icon-shape .label{text-anchor:middle;}#mermaid-svg-e7Poc540IFhxOtfw .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-e7Poc540IFhxOtfw .rough-node .label,#mermaid-svg-e7Poc540IFhxOtfw .node .label,#mermaid-svg-e7Poc540IFhxOtfw .image-shape .label,#mermaid-svg-e7Poc540IFhxOtfw .icon-shape .label{text-align:center;}#mermaid-svg-e7Poc540IFhxOtfw .node.clickable{cursor:pointer;}#mermaid-svg-e7Poc540IFhxOtfw .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-e7Poc540IFhxOtfw .arrowheadPath{fill:#333333;}#mermaid-svg-e7Poc540IFhxOtfw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-e7Poc540IFhxOtfw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-e7Poc540IFhxOtfw .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-e7Poc540IFhxOtfw .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-e7Poc540IFhxOtfw .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-e7Poc540IFhxOtfw .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-e7Poc540IFhxOtfw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-e7Poc540IFhxOtfw .cluster text{fill:#333;}#mermaid-svg-e7Poc540IFhxOtfw .cluster span{color:#333;}#mermaid-svg-e7Poc540IFhxOtfw 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-e7Poc540IFhxOtfw .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-e7Poc540IFhxOtfw rect.text{fill:none;stroke-width:0;}#mermaid-svg-e7Poc540IFhxOtfw .icon-shape,#mermaid-svg-e7Poc540IFhxOtfw .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-e7Poc540IFhxOtfw .icon-shape p,#mermaid-svg-e7Poc540IFhxOtfw .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-e7Poc540IFhxOtfw .icon-shape .label rect,#mermaid-svg-e7Poc540IFhxOtfw .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-e7Poc540IFhxOtfw .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-e7Poc540IFhxOtfw .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-e7Poc540IFhxOtfw :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 第5章
工程化✅
第6章
类型注解装饰器
⬅ 当前
第7章
异步编程
知识体系图
#mermaid-svg-c2jU5QIOaPE1nD7D{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-c2jU5QIOaPE1nD7D .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-c2jU5QIOaPE1nD7D .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-c2jU5QIOaPE1nD7D .error-icon{fill:#552222;}#mermaid-svg-c2jU5QIOaPE1nD7D .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-c2jU5QIOaPE1nD7D .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-c2jU5QIOaPE1nD7D .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-c2jU5QIOaPE1nD7D .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-c2jU5QIOaPE1nD7D .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-c2jU5QIOaPE1nD7D .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-c2jU5QIOaPE1nD7D .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-c2jU5QIOaPE1nD7D .marker{fill:#333333;stroke:#333333;}#mermaid-svg-c2jU5QIOaPE1nD7D .marker.cross{stroke:#333333;}#mermaid-svg-c2jU5QIOaPE1nD7D svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-c2jU5QIOaPE1nD7D p{margin:0;}#mermaid-svg-c2jU5QIOaPE1nD7D .edge{stroke-width:3;}#mermaid-svg-c2jU5QIOaPE1nD7D .section--1 rect,#mermaid-svg-c2jU5QIOaPE1nD7D .section--1 path,#mermaid-svg-c2jU5QIOaPE1nD7D .section--1 circle,#mermaid-svg-c2jU5QIOaPE1nD7D .section--1 polygon,#mermaid-svg-c2jU5QIOaPE1nD7D .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-c2jU5QIOaPE1nD7D .section--1 text{fill:#ffffff;}#mermaid-svg-c2jU5QIOaPE1nD7D .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-c2jU5QIOaPE1nD7D .edge-depth--1{stroke-width:17;}#mermaid-svg-c2jU5QIOaPE1nD7D .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-c2jU5QIOaPE1nD7D .disabled,#mermaid-svg-c2jU5QIOaPE1nD7D .disabled circle,#mermaid-svg-c2jU5QIOaPE1nD7D .disabled text{fill:lightgray;}#mermaid-svg-c2jU5QIOaPE1nD7D .disabled text{fill:#efefef;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-0 rect,#mermaid-svg-c2jU5QIOaPE1nD7D .section-0 path,#mermaid-svg-c2jU5QIOaPE1nD7D .section-0 circle,#mermaid-svg-c2jU5QIOaPE1nD7D .section-0 polygon,#mermaid-svg-c2jU5QIOaPE1nD7D .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-c2jU5QIOaPE1nD7D .section-0 text{fill:black;}#mermaid-svg-c2jU5QIOaPE1nD7D .node-icon-0{font-size:40px;color:black;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-c2jU5QIOaPE1nD7D .edge-depth-0{stroke-width:14;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-c2jU5QIOaPE1nD7D .disabled,#mermaid-svg-c2jU5QIOaPE1nD7D .disabled circle,#mermaid-svg-c2jU5QIOaPE1nD7D .disabled text{fill:lightgray;}#mermaid-svg-c2jU5QIOaPE1nD7D .disabled text{fill:#efefef;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-1 rect,#mermaid-svg-c2jU5QIOaPE1nD7D .section-1 path,#mermaid-svg-c2jU5QIOaPE1nD7D .section-1 circle,#mermaid-svg-c2jU5QIOaPE1nD7D .section-1 polygon,#mermaid-svg-c2jU5QIOaPE1nD7D .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-c2jU5QIOaPE1nD7D .section-1 text{fill:black;}#mermaid-svg-c2jU5QIOaPE1nD7D .node-icon-1{font-size:40px;color:black;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-c2jU5QIOaPE1nD7D .edge-depth-1{stroke-width:11;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-c2jU5QIOaPE1nD7D .disabled,#mermaid-svg-c2jU5QIOaPE1nD7D .disabled circle,#mermaid-svg-c2jU5QIOaPE1nD7D .disabled text{fill:lightgray;}#mermaid-svg-c2jU5QIOaPE1nD7D .disabled text{fill:#efefef;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-2 rect,#mermaid-svg-c2jU5QIOaPE1nD7D .section-2 path,#mermaid-svg-c2jU5QIOaPE1nD7D .section-2 circle,#mermaid-svg-c2jU5QIOaPE1nD7D .section-2 polygon,#mermaid-svg-c2jU5QIOaPE1nD7D .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-c2jU5QIOaPE1nD7D .section-2 text{fill:#ffffff;}#mermaid-svg-c2jU5QIOaPE1nD7D .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-c2jU5QIOaPE1nD7D .edge-depth-2{stroke-width:8;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-c2jU5QIOaPE1nD7D .disabled,#mermaid-svg-c2jU5QIOaPE1nD7D .disabled circle,#mermaid-svg-c2jU5QIOaPE1nD7D .disabled text{fill:lightgray;}#mermaid-svg-c2jU5QIOaPE1nD7D .disabled text{fill:#efefef;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-3 rect,#mermaid-svg-c2jU5QIOaPE1nD7D .section-3 path,#mermaid-svg-c2jU5QIOaPE1nD7D .section-3 circle,#mermaid-svg-c2jU5QIOaPE1nD7D .section-3 polygon,#mermaid-svg-c2jU5QIOaPE1nD7D .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-c2jU5QIOaPE1nD7D .section-3 text{fill:black;}#mermaid-svg-c2jU5QIOaPE1nD7D .node-icon-3{font-size:40px;color:black;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-c2jU5QIOaPE1nD7D .edge-depth-3{stroke-width:5;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-c2jU5QIOaPE1nD7D .disabled,#mermaid-svg-c2jU5QIOaPE1nD7D .disabled circle,#mermaid-svg-c2jU5QIOaPE1nD7D .disabled text{fill:lightgray;}#mermaid-svg-c2jU5QIOaPE1nD7D .disabled text{fill:#efefef;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-4 rect,#mermaid-svg-c2jU5QIOaPE1nD7D .section-4 path,#mermaid-svg-c2jU5QIOaPE1nD7D .section-4 circle,#mermaid-svg-c2jU5QIOaPE1nD7D .section-4 polygon,#mermaid-svg-c2jU5QIOaPE1nD7D .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-c2jU5QIOaPE1nD7D .section-4 text{fill:black;}#mermaid-svg-c2jU5QIOaPE1nD7D .node-icon-4{font-size:40px;color:black;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-c2jU5QIOaPE1nD7D .edge-depth-4{stroke-width:2;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-c2jU5QIOaPE1nD7D .disabled,#mermaid-svg-c2jU5QIOaPE1nD7D .disabled circle,#mermaid-svg-c2jU5QIOaPE1nD7D .disabled text{fill:lightgray;}#mermaid-svg-c2jU5QIOaPE1nD7D .disabled text{fill:#efefef;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-5 rect,#mermaid-svg-c2jU5QIOaPE1nD7D .section-5 path,#mermaid-svg-c2jU5QIOaPE1nD7D .section-5 circle,#mermaid-svg-c2jU5QIOaPE1nD7D .section-5 polygon,#mermaid-svg-c2jU5QIOaPE1nD7D .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-c2jU5QIOaPE1nD7D .section-5 text{fill:black;}#mermaid-svg-c2jU5QIOaPE1nD7D .node-icon-5{font-size:40px;color:black;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-c2jU5QIOaPE1nD7D .edge-depth-5{stroke-width:-1;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-c2jU5QIOaPE1nD7D .disabled,#mermaid-svg-c2jU5QIOaPE1nD7D .disabled circle,#mermaid-svg-c2jU5QIOaPE1nD7D .disabled text{fill:lightgray;}#mermaid-svg-c2jU5QIOaPE1nD7D .disabled text{fill:#efefef;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-6 rect,#mermaid-svg-c2jU5QIOaPE1nD7D .section-6 path,#mermaid-svg-c2jU5QIOaPE1nD7D .section-6 circle,#mermaid-svg-c2jU5QIOaPE1nD7D .section-6 polygon,#mermaid-svg-c2jU5QIOaPE1nD7D .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-c2jU5QIOaPE1nD7D .section-6 text{fill:black;}#mermaid-svg-c2jU5QIOaPE1nD7D .node-icon-6{font-size:40px;color:black;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-c2jU5QIOaPE1nD7D .edge-depth-6{stroke-width:-4;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-c2jU5QIOaPE1nD7D .disabled,#mermaid-svg-c2jU5QIOaPE1nD7D .disabled circle,#mermaid-svg-c2jU5QIOaPE1nD7D .disabled text{fill:lightgray;}#mermaid-svg-c2jU5QIOaPE1nD7D .disabled text{fill:#efefef;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-7 rect,#mermaid-svg-c2jU5QIOaPE1nD7D .section-7 path,#mermaid-svg-c2jU5QIOaPE1nD7D .section-7 circle,#mermaid-svg-c2jU5QIOaPE1nD7D .section-7 polygon,#mermaid-svg-c2jU5QIOaPE1nD7D .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-c2jU5QIOaPE1nD7D .section-7 text{fill:black;}#mermaid-svg-c2jU5QIOaPE1nD7D .node-icon-7{font-size:40px;color:black;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-c2jU5QIOaPE1nD7D .edge-depth-7{stroke-width:-7;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-c2jU5QIOaPE1nD7D .disabled,#mermaid-svg-c2jU5QIOaPE1nD7D .disabled circle,#mermaid-svg-c2jU5QIOaPE1nD7D .disabled text{fill:lightgray;}#mermaid-svg-c2jU5QIOaPE1nD7D .disabled text{fill:#efefef;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-8 rect,#mermaid-svg-c2jU5QIOaPE1nD7D .section-8 path,#mermaid-svg-c2jU5QIOaPE1nD7D .section-8 circle,#mermaid-svg-c2jU5QIOaPE1nD7D .section-8 polygon,#mermaid-svg-c2jU5QIOaPE1nD7D .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-c2jU5QIOaPE1nD7D .section-8 text{fill:black;}#mermaid-svg-c2jU5QIOaPE1nD7D .node-icon-8{font-size:40px;color:black;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-c2jU5QIOaPE1nD7D .edge-depth-8{stroke-width:-10;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-c2jU5QIOaPE1nD7D .disabled,#mermaid-svg-c2jU5QIOaPE1nD7D .disabled circle,#mermaid-svg-c2jU5QIOaPE1nD7D .disabled text{fill:lightgray;}#mermaid-svg-c2jU5QIOaPE1nD7D .disabled text{fill:#efefef;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-9 rect,#mermaid-svg-c2jU5QIOaPE1nD7D .section-9 path,#mermaid-svg-c2jU5QIOaPE1nD7D .section-9 circle,#mermaid-svg-c2jU5QIOaPE1nD7D .section-9 polygon,#mermaid-svg-c2jU5QIOaPE1nD7D .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-c2jU5QIOaPE1nD7D .section-9 text{fill:black;}#mermaid-svg-c2jU5QIOaPE1nD7D .node-icon-9{font-size:40px;color:black;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-c2jU5QIOaPE1nD7D .edge-depth-9{stroke-width:-13;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-c2jU5QIOaPE1nD7D .disabled,#mermaid-svg-c2jU5QIOaPE1nD7D .disabled circle,#mermaid-svg-c2jU5QIOaPE1nD7D .disabled text{fill:lightgray;}#mermaid-svg-c2jU5QIOaPE1nD7D .disabled text{fill:#efefef;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-10 rect,#mermaid-svg-c2jU5QIOaPE1nD7D .section-10 path,#mermaid-svg-c2jU5QIOaPE1nD7D .section-10 circle,#mermaid-svg-c2jU5QIOaPE1nD7D .section-10 polygon,#mermaid-svg-c2jU5QIOaPE1nD7D .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-c2jU5QIOaPE1nD7D .section-10 text{fill:black;}#mermaid-svg-c2jU5QIOaPE1nD7D .node-icon-10{font-size:40px;color:black;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-c2jU5QIOaPE1nD7D .edge-depth-10{stroke-width:-16;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-c2jU5QIOaPE1nD7D .disabled,#mermaid-svg-c2jU5QIOaPE1nD7D .disabled circle,#mermaid-svg-c2jU5QIOaPE1nD7D .disabled text{fill:lightgray;}#mermaid-svg-c2jU5QIOaPE1nD7D .disabled text{fill:#efefef;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-root rect,#mermaid-svg-c2jU5QIOaPE1nD7D .section-root path,#mermaid-svg-c2jU5QIOaPE1nD7D .section-root circle,#mermaid-svg-c2jU5QIOaPE1nD7D .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-c2jU5QIOaPE1nD7D .section-root text{fill:#ffffff;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-root span{color:#ffffff;}#mermaid-svg-c2jU5QIOaPE1nD7D .section-2 span{color:#ffffff;}#mermaid-svg-c2jU5QIOaPE1nD7D .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-c2jU5QIOaPE1nD7D .edge{fill:none;}#mermaid-svg-c2jU5QIOaPE1nD7D .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-c2jU5QIOaPE1nD7D :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 第6章:类型与装饰器
类型注解
基础注解
Optional_Union
TypeVar_Generic
TypedDict
Protocol
Pydantic v2
模型定义
验证器
序列化
mypy
静态检查
配置
装饰器
原理 闭包
自定义装饰器
带参数装饰器
functools.wraps
常用装饰器
生成器
yield
yield from
生成器表达式
一、核心知识
1.1 类型注解基础
Python 的类型注解是可选的、不影响运行时的元数据------但配合 Pydantic 和 mypy,它能提供媲美静态语言的安全感:
python
# 基础注解
name: str = "Agent"
version: float = 1.0
is_active: bool = True
scores: list[int] = [85, 92, 78]
config: dict[str, str | int] = {"model": "gpt-4o", "tokens": 4096}
# 函数注解
def process_message(
content: str,
temperature: float = 0.7,
tools: list[str] | None = None,
) -> str:
"""content 必传,tools 可选,返回 str"""
return content.upper()
# Optional[X] = X | None
from typing import Optional
def find_agent(name: str) -> Optional[dict]:
...
# Union------Python 3.10+ 用 | 替代
from typing import Union
def get_value() -> Union[str, int]: ... # 旧
def get_value() -> str | int: ... # 新(推荐)
1.2 进阶类型
python
from typing import TypeVar, Generic, Protocol, TypedDict
# TypeVar------泛型变量
T = TypeVar("T")
class Repository(Generic[T]):
def get(self, id: str) -> T | None: ...
def save(self, entity: T) -> None: ...
# 使用------类型安全!
agent_repo: Repository["Agent"] = Repository()
agent = agent_repo.get("1") # IDE 知道 agent 是 Agent 类型
# Protocol------结构化类型(鸭子类型的类型注解版)
class Processable(Protocol):
def process(self, input: str) -> str: ...
def run_pipeline(processor: Processable, messages: list[str]):
return [processor.process(m) for m in messages]
# TypedDict------类型化的字典
class AgentConfig(TypedDict):
name: str
model: str
temperature: float
1.3 Pydantic v2------运行时验证
Pydantic 是 Python 数据验证的事实标准。它比 @dataclass 更强大------自动类型转换、验证、序列化:
python
from pydantic import BaseModel, Field, field_validator
from datetime import datetime
from enum import Enum
class AgentStatus(str, Enum):
ACTIVE = "active"
INACTIVE = "inactive"
class AgentCreate(BaseModel):
"""创建 Agent 的请求模型"""
name: str = Field(..., min_length=2, max_length=50, description="Agent 名称")
model: str = Field(default="gpt-4o", description="模型名称")
temperature: float = Field(default=0.7, ge=0, le=2)
tools: list[str] = Field(default_factory=list)
@field_validator("name")
@classmethod
def name_must_not_contain_special_chars(cls, v: str) -> str:
if "<" in v or ">" in v:
raise ValueError("Name contains invalid characters")
return v.strip()
class AgentResponse(BaseModel):
"""Agent 响应模型"""
id: str
name: str
model: str
status: AgentStatus
created_at: datetime
model_config = {"from_attributes": True} # 可从 ORM 对象创建
# 使用
data = {"name": " assistant ", "temperature": 0.9, "tools": ["search"]}
agent = AgentCreate(**data)
print(agent.name) # "assistant" ------ 自动去空白
print(agent.model_dump()) # 序列化为 dict
# 如果 name="a" --- ValidationError: 长度至少为 2
二、详细知识讲解
2.1 装饰器原理
装饰器本质上是一个接受函数、返回新函数的高阶函数:
python
import functools
import time
# 基础装饰器
def log_execution(func):
"""记录函数执行日志"""
@functools.wraps(func) # 保留原函数的元数据(__name__、__doc__)
def wrapper(*args, **kwargs):
print(f"[LOG] Calling {func.__name__}")
result = func(*args, **kwargs)
print(f"[LOG] {func.__name__} returned {result}")
return result
return wrapper
@log_execution # @ 语法糖等价于 greet = log_execution(greet)
def greet(name: str) -> str:
"""Say hello"""
return f"Hello, {name}"
print(greet.__name__) # "greet"(有 @wraps)
print(greet("Agent")) # [LOG] Calling greet / [LOG] greet returned Hello, Agent
带参数的装饰器(三层嵌套)
python
# 装饰器工厂------接受参数,返回真正的装饰器
def retry(max_attempts: int = 3, delay: float = 1.0):
"""失败自动重试"""
import time as _time
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
last_error = None
for attempt in range(1, max_attempts + 1):
try:
return func(*args, **kwargs)
except Exception as e:
last_error = e
if attempt < max_attempts:
print(f"Retry {attempt}/{max_attempts} after {delay}s...")
_time.sleep(delay)
raise last_error
return wrapper
return decorator
@retry(max_attempts=3, delay=0.5)
def call_llm(prompt: str) -> str:
# 模拟不稳定调用
import random
if random.random() < 0.7:
raise ConnectionError("Network error")
return f"Response: {prompt}"
常用装饰器实现
python
# 缓存装饰器
def cache_result(func):
"""简单内存缓存"""
cache = {}
@functools.wraps(func)
def wrapper(*args, **kwargs):
key = (args, tuple(sorted(kwargs.items())))
if key not in cache:
cache[key] = func(*args, **kwargs)
return cache[key]
return wrapper
# 计时装饰器
def timer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{func.__name__} took {elapsed:.4f}s")
return result
return wrapper
2.2 生成器
生成器是惰性求值的迭代器 ,用 yield 而不是 return。它在 AI Agent 开发中极其重要------流式处理 LLM 输出:
python
# 生成器函数
def generate_ids(count: int):
"""逐个生成 ID,不一次性创建列表"""
for i in range(count):
yield f"agent_{i:04d}"
# 惰性迭代------内存占用极小
for agent_id in generate_ids(1_000_000):
if agent_id == "agent_0003":
print(agent_id)
break # 只生成了 4 个,不是 100 万个
# yield from------委托给子生成器
def generate_all():
yield from generate_ids(3)
yield from ["special_1", "special_2"]
生成器表达式
python
# 列表------一次性创建,占用内存
squares_list = [x**2 for x in range(1_000_000)] # 内存爆炸
# 生成器表达式------惰性求值
squares_gen = (x**2 for x in range(1_000_000)) # 几乎不占内存
print(next(squares_gen)) # 1
print(next(squares_gen)) # 4
# 管道式处理(每个元素流经整个管道,而非每步全部处理完)
messages = (m.strip() for m in raw_lines)
non_empty = (m for m in messages if m)
processed = (f"[{m}]" for m in non_empty)
for msg in processed:
print(msg)
2.3 迭代器协议
python
# 任何实现了 __iter__ 和 __next__ 的对象都是迭代器
class ConversationStream:
"""对话流迭代器"""
def __init__(self, messages: list[str]):
self.messages = messages
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.messages):
raise StopIteration
msg = self.messages[self.index]
self.index += 1
return msg
# for 循环自动使用迭代器协议
for msg in ConversationStream(["Hi", "How are you?"]):
print(msg)
2.4 @staticmethod 与 @classmethod 对比
两者都是定义在类内部的"特殊方法",但用途完全不同。Java 开发者容易混淆------本节一次性讲清楚:
python
class AgentFactory:
"""Agent 工厂------展示三种方法类型的实际使用"""
default_model = "gpt-4o" # 类变量
def __init__(self, api_key: str):
"""实例方法------第一个参数是 self(实例本身)"""
self.api_key = api_key
def create_agent(self, name: str) -> dict:
"""实例方法:可以访问 self.api_key 和 self.default_model"""
return {
"name": name,
"model": self.default_model,
"api_key_prefix": self.api_key[:7],
}
@classmethod
def from_env(cls) -> "AgentFactory":
"""类方法------第一个参数是 cls(类本身),可以访问类变量。
典型用途:替代构造函数(工厂方法模式)"""
import os
api_key = os.getenv("OPENAI_API_KEY", "sk-default")
return cls(api_key)
@classmethod
def with_model(cls, model: str) -> "AgentFactory":
"""类方法:创建特定配置的工厂"""
instance = cls("sk-default")
instance.default_model = model
return instance
@staticmethod
def validate_name(name: str) -> bool:
"""静态方法------不需要 self 也不需要 cls,纯工具函数。
放在类内部只是逻辑上属于这个类,本质和模块级函数无异。
典型用途:工具/验证函数、工厂辅助逻辑"""
return 2 <= len(name.strip()) <= 50
# === 三种方法的调用方式 ===
# 实例方法:通过实例调用
factory = AgentFactory("sk-abc123")
agent = factory.create_agent("MyAgent") # ✅
# 类方法:通过类调用(自动传入 cls)
factory2 = AgentFactory.from_env() # ✅ 替代构造函数
# 静态方法:通过类或实例都可调用
AgentFactory.validate_name("MyAgent") # ✅ 类调用
factory.validate_name("MyAgent") # ✅ 实例调用(但不推荐)
| 方法类型 | 第一个参数 | 可访问类变量 | 可访问实例变量 | 典型用途 |
|---|---|---|---|---|
实例方法 def f(self) |
self(实例) |
✅ | ✅ | 核心业务逻辑 |
类方法 @classmethod |
cls(类) |
✅ | ❌ | 替代构造函数、工厂模式 |
静态方法 @staticmethod |
无 | ❌ | ❌ | 工具函数、验证逻辑 |
Java 对照 :
@classmethod≈ Java 的静态工厂方法(如LocalDate.of()),@staticmethod≈ Java 的static方法。关键区别:Python 的@classmethod可以被子类继承并传入子类的cls,实现多态。
2.5 contextlib.contextmanager ------ 上下文管理器装饰器
在第 4 章我们学习了 with 语句和 __enter__/__exit__ 魔术方法。@contextmanager 提供了更简洁的方式:
python
from contextlib import contextmanager
import time
# ===== 方式 1:基于类的上下文管理器(第 4 章回顾) =====
class Timer:
"""计时器------with Timer() as t: ..."""
def __enter__(self):
self.start = time.perf_counter()
return self
def __exit__(self, *args):
self.elapsed = time.perf_counter() - self.start
print(f"耗时: {self.elapsed:.4f}s")
# ===== 方式 2:@contextmanager 装饰器 ------ 更简洁! =====
@contextmanager
def timer(name: str = "操作"):
"""计时上下文管理器------yield 前是 __enter__,yield 后是 __exit__"""
start = time.perf_counter()
print(f"[{name}] 开始...")
try:
yield # ← 控制权交给 with 块内的代码
finally:
elapsed = time.perf_counter() - start
print(f"[{name}] 完成,耗时 {elapsed:.4f}s")
# 使用
with timer("LLM调用"):
time.sleep(0.1)
# 输出:
# [LLM调用] 开始...
# [LLM调用] 完成,耗时 0.1002s
# ===== 实用案例:临时修改环境变量 =====
import os
@contextmanager
def set_env(**kwargs):
"""临时设置环境变量,with 块结束后自动恢复"""
original = {}
for key, value in kwargs.items():
original[key] = os.environ.get(key)
os.environ[key] = value
try:
yield
finally:
for key, value in original.items():
if value is None:
os.environ.pop(key, None)
else:
os.environ[key] = value
# 使用
with set_env(LOG_LEVEL="DEBUG", TIMEOUT="60"):
print(os.environ["LOG_LEVEL"]) # "DEBUG"
print(os.environ.get("LOG_LEVEL", "未设置")) # "未设置"(已恢复)
# ===== 实用案例:数据库事务管理 =====
@contextmanager
def db_transaction(connection):
"""自动管理数据库事务:成功则提交,失败则回滚"""
print("BEGIN TRANSACTION")
try:
yield connection
print("COMMIT")
except Exception:
print("ROLLBACK")
raise
# 模拟使用
class FakeDB:
def execute(self, sql): print(f" SQL: {sql}")
db = FakeDB()
with db_transaction(db) as conn:
conn.execute("INSERT INTO agents VALUES (1, 'test')")
conn.execute("UPDATE agents SET status='active' WHERE id=1")
# 输出:
# BEGIN TRANSACTION
# SQL: INSERT INTO agents VALUES (1, 'test')
# SQL: UPDATE agents SET status='active' WHERE id=1
# COMMIT
Java 对照 :
@contextmanager≈ Java 的try-with-resources+AutoCloseable,但更灵活------可以用在任意逻辑上,不限于资源释放。
2.6 mypy 实战------Python 的"编译时"类型检查
mypy 是 Python 社区事实标准的静态类型检查工具。它不运行代码,只检查类型注解的正确性------相当于 Java 编译器做的类型检查,但是可选的、渐进式的:
python
# ===== mypy 会捕获的错误 =====
# ❌ 类型不匹配
def greet(name: str) -> str:
return 42 # mypy: error: Incompatible return value type (got "int", expected "str")
# ❌ 可能为 None 但未检查
def process(data: str | None) -> str:
return data.upper() # mypy: error: Item "None" of "str | None" has no attribute "upper"
# ✅ 正确:先检查 None
def process_safe(data: str | None) -> str:
if data is None:
return ""
return data.upper()
# ❌ 参数类型不匹配
def create_agent(name: str, temperature: float) -> dict:
return {"name": name, "temp": temperature}
create_agent(123, "0.7") # mypy: 两个参数类型都错了!
mypy 配置
toml
# pyproject.toml 中添加 mypy 配置
[tool.mypy]
python_version = "3.12"
strict = false # 逐步启用严格模式
# 基础检查(建议全部开启)
warn_return_any = true
warn_unused_configs = true
warn_redundant_casts = true
warn_unused_ignores = true
no_implicit_optional = true
# 函数相关
disallow_untyped_defs = true # 所有函数必须有类型注解
disallow_incomplete_defs = true # 所有参数必须有类型注解
check_untyped_defs = true
# 严格模式(企业项目推荐逐步启用)
# strict = true # 开启所有严格检查
# 第三方库的类型存根
[[tool.mypy.overrides]]
module = ["pydantic", "fastapi", "httpx"]
ignore_missing_imports = true # 缺少类型存根的库忽略
渐进式采用策略
bash
# 第 1 步:安装 mypy
uv add --dev mypy
# 第 2 步:在项目中运行(会看到很多错误,正常)
uv run mypy src/
# 第 3 步:先处理公共 API 的类型错误
uv run mypy src/agent_platform/api/ src/agent_platform/services/
# 第 4 步:逐步启用严格检查
# 在 pyproject.toml 中逐步开启 disallow_untyped_defs 等选项
# 第 5 步:加入 CI/CD 流程(可选)
# uv run mypy src/ --strict
Java 对照:mypy ≈ Java 编译器的类型检查阶段,但它不阻止代码运行------即使 mypy 报错,代码仍然可以正常执行。这体现了 Python "类型注解是可选元数据" 的设计哲学。
三、代码示例
示例 1:Pydantic 验证的 Agent 配置系统
python
from pydantic import BaseModel, Field, model_validator
from typing import Any
class LLMConfig(BaseModel):
model: str = "gpt-4o"
temperature: float = Field(default=0.7, ge=0, le=2)
max_tokens: int = Field(default=4096, gt=0)
class AgentConfig(BaseModel):
name: str = Field(..., min_length=2, max_length=50)
llm: LLMConfig = Field(default_factory=LLMConfig)
tools: list[str] = Field(default_factory=list)
memory_enabled: bool = False
@model_validator(mode="after")
def validate_tools(self) -> "AgentConfig":
if "search" in self.tools and not self.memory_enabled:
raise ValueError("Search tool requires memory_enabled=True")
return self
# 验证通过
config = AgentConfig(name="assistant", llm={"temperature": 0.9})
print(config.model_dump())
# 验证失败
try:
AgentConfig(name="a", tools=["search"]) # name 太短 + search 需要 memory
except Exception as e:
print(e)
示例 2:可组合的装饰器栈
python
import functools, time, logging
def log_call(level=logging.INFO):
def deco(func):
@functools.wraps(func)
def wrapper(*a, **kw):
logging.log(level, f"→ {func.__name__}")
r = func(*a, **kw)
logging.log(level, f"← {func.__name__}")
return r
return wrapper
return deco
def retry(max_times=3):
def deco(func):
@functools.wraps(func)
def wrapper(*a, **kw):
for i in range(max_times):
try: return func(*a, **kw)
except Exception as e:
if i == max_times - 1: raise
time.sleep(0.5)
return wrapper
return deco
@log_call()
@retry(max_times=2)
def unstable_api_call(text: str) -> str:
import random
if random.random() < 0.5:
raise ConnectionError("fail")
return f"OK: {text}"
# 执行顺序:log_call(retry(original))
# 1. log_call wrapper 进入
# 2. retry wrapper 执行(含重试逻辑)
# 3. 原始函数执行
# 4. retry wrapper 退出
# 5. log_call wrapper 退出
四、实战案例
案例 1:Token 用量追踪装饰器
python
import functools, time
from collections import defaultdict
# 全局统计
token_stats: dict[str, dict] = defaultdict(lambda: {"calls": 0, "tokens": 0})
def track_tokens(agent_name: str):
"""追踪 Agent 的 Token 用量"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
# 模拟从返回值提取 token 数
tokens = len(str(result)) // 4
token_stats[agent_name]["calls"] += 1
token_stats[agent_name]["tokens"] += tokens
print(f"[{agent_name}] +{tokens} tokens ({elapsed:.2f}s) | "
f"total: {token_stats[agent_name]['tokens']}")
return result
return wrapper
return decorator
@track_tokens("assistant")
def call_gpt(prompt: str) -> str:
time.sleep(0.1)
return f"Response to: {prompt}"
call_gpt("Hello")
call_gpt("How are you?")
案例 2:生成器实现流式对话
python
from typing import Generator
import time
def stream_chat_response(prompt: str) -> Generator[str, None, None]:
"""模拟 LLM 逐 Token 流式输出"""
response = f"Based on your question '{prompt[:20]}...', here is my analysis: "
words = response.split()
for word in words:
yield word + " "
time.sleep(0.05) # 模拟网络延迟
yield "\n\nKey points:\n"
for i, point in enumerate(["Point one", "Point two", "Point three"], 1):
yield f"{i}. {point}\n"
time.sleep(0.1)
# 流式消费
print("🤖 Agent: ", end="", flush=True)
for chunk in stream_chat_response("Tell me about Python"):
print(chunk, end="", flush=True)
五、Java 开发者对照学习
| 概念 | Java | Python |
|---|---|---|
| 类型泛型 | <T> |
TypeVar("T") + Generic[T] |
| 接口类型 | interface Processable |
Protocol |
| AOP | Spring AOP / Proxy / Interceptor | 装饰器 |
| 数据验证 | Bean Validation / @NotNull |
Pydantic Field(...) |
| 静态检查 | 编译器 | mypy / pyright |
| 惰性序列 | Stream<T> |
生成器 (x for x in ...) |
| 注解 | @Annotation(元数据) |
@decorator(修改行为) |
装饰器 vs Java 注解:
- Java 注解是元数据------需要框架(Spring/反射)解释执行
- Python 装饰器是运行时代码------直接替换/包装函数,无需额外框架
六、企业最佳实践
- Pydantic > dataclass:当需要验证、序列化、OpenAPI schema 时
- 装饰器保持单一职责:一个装饰器只做一件事
- 永远使用
@functools.wraps:保留函数签名和文档 yield流式处理大数据:LLM 输出、文件读取、ETL 管道- 类型注解渐进式:先公共 API,再内部函数,最后复杂泛型
七、常见错误
| 错误 | 说明 |
|---|---|
装饰器忘记 @wraps |
丢失函数名和文档字符串 |
| 生成器只遍历一次 | (x for x in data) 消费后为空 |
TypeVar 忘记绑定 |
泛型失去类型约束 |
| Pydantic 模型可变默认值 | list/dict 用 default_factory |
八、本章总结
类型注解让你重获类型安全感;Pydantic 是数据验证的终极武器;装饰器替代了 Java 的 AOP;生成器是大数据和流式处理的基石。
九、面试题
- 装饰器的本质是什么?
@functools.wraps的作用? TypeVar和 Java 的<T>的异同?- 生成器和列表的区别?何时用生成器?
- Pydantic
BaseModel和@dataclass的核心区别? - 如何实现带参数的装饰器?
十、练习题
- 为
AgentService类的方法添加完整的类型注解 - 实现
@validate_input装饰器------用 Pydantic 验证函数参数 - 用生成器实现一个"分页数据加载器"
- 实现
@singleton装饰器 - 定义
MessageProtocol------用 Protocol 约束消息处理接口 - 用 Pydantic 定义
ToolCall和ToolResult模型 - 实现
@deprecated装饰器 - 写一个生成器实现斐波那契数列
- 用
TypedDict定义 LLM API 响应的类型 - 实现装饰器组合工具函数
十一、今日作业
基础:为项目所有公共函数/类添加类型注解
中等 :实现 @retry、@log_call、@cache_result 三个装饰器并应用到 Agent 服务中
挑战 :实现 @validate_response(pydantic_model) 装饰器------自动验证函数返回值是否符合 Pydantic 模型
十二、预习下一章
第 7 章:异步编程------asyncio 事件循环、async/await、协程、并发控制。这是调用 LLM API(IO 密集型)的核心技能。