书架 · Python 学习系列 · 02 · Python 进阶下一章:03 工程化与异步 →
02 · Python 进阶
本章目标:掌握面向对象、装饰器、生成器、上下文管理器、类型提示、常用标准库,以及新手必踩的坑。学完你能读懂主流框架(FastAPI / openai SDK / LangChain)的源码风格。
前置:学完 01-Python基础。
目录
1. 面向对象
1.1 类的定义
class User:
# 类属性(≈ static 字段,所有实例共享)
species = "human"
# 构造方法。self 就是 Java 里的 this,但必须显式写在第一个参数
def __init__(self, name: str, age: int):
# 实例属性(≈ 成员字段)
self.name = name
self.age = age
# 实例方法:第一个参数永远是 self
def greet(self) -> str:
return f"我是 {self.name}"
# 魔术方法:print(obj) 时自动调用,≈ Java 的 toString()
def __repr__(self) -> str:
return f"User(name={self.name!r}, age={self.age})"
# == 判断时调用,≈ Java 的 equals()
def __eq__(self, other) -> bool:
if not isinstance(other, User):
return NotImplemented
return self.name == other.name
u = User("macao", 18)
print(u) # User(name='macao', age=18)
u2 = User("macao", 20)
print(u == u2) # True(只比 name)
☕ Java vs Python OOP 对照: - 没有
public/private/protected:Python 靠约定——_name单下划线表示"内部用",__name双下划线触发名称改写(name mangling,伪私有)。全靠自觉,这在 Python 文化里叫 "we're all consenting adults" -self≈this,但必须显式传:调用u.greet()实际执行User.greet(u)-__init__≈ 构造器;__repr__/__str__≈toString();__eq__≈equals()(注意:定义了__eq__后 hash 行为会变,可变对象默认不实现__hash__) - 没有真正的私有方法和 final,也没有接口interface(用 Protocol,见下节)
1.2 继承与多态
class Animal:
def __init__(self, name: str):
self.name = name
def speak(self) -> str:
raise NotImplementedError # ≈ 抽象方法的朴素写法
class Dog(Animal): # 括号里写父类,没有 extends 关键字
def speak(self) -> str:
return f"{self.name}: 汪汪"
class Cat(Animal):
def __init__(self, name: str, indoor: bool):
super().__init__(name) # super() 调父类构造,同 Java
self.indoor = indoor
def speak(self) -> str:
return f"{self.name}: 喵"
# 多态不需要共同接口/继承声明——能 speak 就行(见鸭子类型)
for a in [Dog("旺财"), Cat("咪咪", False)]: # Cat 的 indoor 无默认值,必须显式传
print(a.speak())
Python 支持多继承(class A(B, C),方法查找按 MRO 顺序)。AI 框架源码里常见 class ChatAgent(BaseAgent[dict]) 这种写法——继承 + 泛型参数。初学阶段用单继承就够,知道多继承存在即可。
1.3 抽象基类(正统写法)
from abc import ABC, abstractmethod
class Storage(ABC): # ≈ abstract class
@abstractmethod
def save(self, key: str, value: str) -> None:
...
class MemoryStorage(Storage):
def save(self, key: str, value: str) -> None:
print(f"存到内存: {key}={value}")
# Storage() 直接实例化会报 TypeError
1.4 property:把方法伪装成字段
class Circle:
def __init__(self, r: float):
self._r = r # 惯例:内部字段加 _
@property # 读 c.area 像字段,实际执行方法
def area(self) -> float:
return 3.14159 * self._r ** 2
@property
def radius(self) -> float:
return self._r
@radius.setter # c.radius = 10 时触发校验
def radius(self, value: float):
if value <= 0:
raise ValueError("半径必须为正")
self._r = value
c = Circle(2)
print(c.area) # 12.566... ← 注意没有括号
c.radius = 5 # 走 setter 校验
☕ ≈ Lombok 的 getter/setter 或 Kotlin 的属性,但 Python 从第一天就建议:字段直接 public 暴露,需要校验时再无痛升级成 property——调用方代码一行不用改(这也是 Python 不写 getter/setter 的原因)。
1.5 dataclass:数据类的正确打开方式
from dataclasses import dataclass, field
@dataclass
class ChatMessage:
role: str
content: str
tokens: int = 0
meta: dict = field(default_factory=dict) # 可变默认值的正确写法
msg = ChatMessage(role="user", content="你好")
print(msg) # 自动生成 __repr__
print(msg == ChatMessage("user", "你好")) # 自动生成 __eq__
# 不可变版本(≈ Java record)
@dataclass(frozen=True)
class Point:
x: float
y: float
☕
@dataclass≈ Lombok@Data/ Java 16+record:自动生成__init__、__repr__、__eq__。openai SDK、FastAPI 的请求模型全是这套思路(Pydantic 是它的加强版,见第 07 章)。AI 开发里你天天会定义Message、ToolCall这类数据类,优先用 dataclass 或 pydantic,别手写。
2. 鸭子类型与 Protocol
"如果它走起来像鸭子、叫起来像鸭子,那它就是鸭子。"——Python 不关心对象的类型,只关心它能做什么。
def total(items):
# 不检查类型,只要能迭代、元素支持 + 就行
# list、tuple、生成器、numpy 数组……都能传
return sum(items)
☕ Java 是名义类型(nominal typing):必须
implements Comparable才算数。Python 是结构类型(structural typing):只要长了那个方法就算。这就是为什么 Python 不需要接口也能多态。
需要"接口"的约束感时,用 typing.Protocol(静态检查工具认,运行时不强制):
from typing import Protocol
class Speakable(Protocol):
def speak(self) -> str: ...
def make_sound(x: Speakable) -> None: # 任何有 speak() 方法的对象都满足
print(x.speak())
3. 闭包与装饰器
3.1 函数是一等公民
# 函数可以赋值给变量、当参数传、当返回值
def shout(text: str) -> str:
return text.upper() + "!"
func = shout # 注意没有括号——传的是函数本身
print(func("hello")) # HELLO!
def apply(f, value): # 高阶函数:接收函数
return f(value)
apply(shout, "hey") # 'HEY!'
☕ Java 8 之后
Function<String,String>/ lambda 是"模拟"一等函数,Python 里函数天生就是对象。
3.2 闭包
def make_counter():
count = 0
def increment():
nonlocal count # 闭包引用外层函数的变量
count += 1
return count
return increment
counter = make_counter()
counter() # 1
counter() # 2 ← 状态被闭包"记住"了
3.3 装饰器(重点,框架里到处都是)
装饰器 = 接收一个函数、返回增强版函数的函数。@xxx 只是语法糖:
import functools
import time
def timer(func):
@functools.wraps(func) # 保住原函数的名字和文档
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
cost = time.perf_counter() - start
print(f"{func.__name__} 耗时 {cost:.3f}s")
return result
return wrapper
# @timer 等价于 slow_add = timer(slow_add)
@timer
def slow_add(a, b):
time.sleep(0.5)
return a + b
slow_add(1, 2) # slow_add 耗时 0.500s
带参数的装饰器(三层嵌套):
import functools
def retry(times: int):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for i in range(times):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"第 {i + 1} 次失败: {e}")
raise RuntimeError(f"{func.__name__} 重试 {times} 次仍失败")
return wrapper
return decorator
@retry(times=3)
def call_api(): ...
☕ Java vs Python: - 装饰器 ≈ Java 注解 + AOP 的合体,但没有代理、没有反射、没有 IoC 容器——就是普通的函数包裹,单步就能调试进去 - FastAPI 的
@app.get("/users")、openai SDK 无关的各类@tool、pytest 的@pytest.fixture,本质全是本章这个wrapper- 记住公式:@deco⇔func = deco(func);@deco(arg)⇔func = deco(arg)(func)
4. 迭代器与生成器
4.1 协议
# 可迭代对象(Iterable):实现了 __iter__
# 迭代器(Iterator):实现了 __iter__ 和 __next__
it = iter([1, 2, 3]) # 从可迭代对象取出迭代器
next(it) # 1
next(it) # 2
next(it) # 3
# next(it) # StopIteration 异常
☕ ≈ Java 的
Iterable/Iterator,连方法名都几乎一样(__iter__≈iterator(),__next__≈next())。
4.2 生成器:惰性产出的函数
函数体里出现 yield,它就变成生成器函数——调用时不执行,遍历时才逐个产出:
def read_large_file(path):
with open(path, encoding="utf-8") as f:
for line in f:
yield line.strip() # 产出一行,暂停,等下一次索取
# 先准备示例文件(真实场景是 10GB 日志)
with open("huge.log", "w", encoding="utf-8") as f:
f.write("INFO 启动\nINFO 处理请求\nDONE\n")
# 10GB 的文件也不会撑爆内存——同一时刻内存里只有一行
for line in read_large_file("huge.log"):
print(line)
# 生成器表达式:推导式的惰性版(圆括号)
total = sum(x * x for x in range(10_000_000)) # 不建 1000 万元素的 list
print(total)
4.3 yield from 与无限序列
def chain(*iterables):
for it in iterables:
yield from it # 逐个产出子可迭代对象的元素
list(chain([1, 2], "ab")) # [1, 2, 'a', 'b']
from itertools import count
evens = (x for x in count() if x % 2 == 0) # 无限偶数流,用时才算
为什么 AI 开发要懂生成器:LLM 流式输出的处理就是"拿一个生成器逐 chunk 消费";FastAPI 的流式响应(SSE)接口函数就是一个
async生成器。见第 04、07 章。
5. 上下文管理器 with
with open("a.txt", "w", encoding="utf-8") as f: # 先准备文件
f.write("第一行内容")
# with 块结束(或异常)时自动调用 f.__exit__() → 自动关资源
with open("a.txt", encoding="utf-8") as f:
data = f.read()
# 这里文件已自动关闭,即使中间抛了异常
print(data)
☕ ≈ Java 7 的 try-with-resources,但不止管资源——任何"进入/退出"语义都能用:数据库事务、锁、临时目录、HTTP 连接。
自己实现:
import time
class Timer:
def __enter__(self):
self.start = time.perf_counter()
return self # as 后面拿到的就是它
def __exit__(self, exc_type, exc_val, exc_tb):
print(f"耗时 {time.perf_counter() - self.start:.3f}s")
return False # False = 异常继续往外抛
with Timer():
time.sleep(0.2)
更省事的写法:
import time
from contextlib import contextmanager
@contextmanager
def timer():
start = time.perf_counter()
try:
yield # yield 之前 = __enter__,之后 = __exit__
finally:
print(f"耗时 {time.perf_counter() - start:.3f}s")
with timer():
time.sleep(0.2) # 换成你的业务代码
6. 类型提示 typing
Python 3.5+ 支持"类型提示":运行时不强制(写错不会报错),但 IDE 补全、静态检查(mypy / pyright)、以及 FastAPI / Pydantic 的运行时校验全靠它。现代 Python 项目必写。
# 基础:参数和返回值
def process(items: list[str], limit: int = 10) -> dict[str, int]: ...
# 3.10+ 可以用 | 代替 Union
class User: # 前文 1.1 定义过的类,这里补个最小实现
pass
def find(name: str | None = None) -> User | None: ...
# 常用容器与结构
from typing import Any, Callable, Optional
def handler(fn: Callable[[int], str], data: list[dict[str, Any]]) -> tuple[bool, str]: ...
# 类型别名(3.12+ 直接用 type 语句)
type Messages = list[dict[str, str]]
# 泛型函数
def first[T](items: list[T]) -> T: # 3.12+ 新语法
return items[0]
# TypedStruct 风格:用 TypedDict 描述 dict 结构(AI 开发常用!)
from typing import TypedDict
class Message(TypedDict):
role: str
content: str
msgs: list[Message] = [{"role": "user", "content": "hi"}]
☕ Java vs Python 类型系统: - Java 类型是编译期强制的;Python 类型提示是可选元数据——但写了之后体验接近 Java:IDE 补全、重构、检查都有 -
str | None≈@Nullable String;list[dict[str, Any]]≈List<Map<String, Object>>- FastAPI 直接拿类型提示做请求校验 + 自动生成 API 文档,这是"写 Python 要带类型"的最强理由(第 07 章) - 官方手册:typing 模块文档(含各版本语法变化)
7. 常用标准库速查
collections —— 增强容器
from collections import Counter, defaultdict, deque
# Counter:计数神器(AI 分词统计、日志分析)
Counter("apple apple banana".split()) # Counter({'apple': 2, 'banana': 1})
words = "apple cherry banana banana apple banana".split()
Counter(words).most_common(3) # 前 3 高频
# defaultdict:带默认值的 dict,免去 KeyError 判断
groups = defaultdict(list)
for name, dept in [("a", "dev"), ("b", "ops"), ("c", "dev")]:
groups[dept].append(name) # 不用先判断 key 存不存在
# deque:双端队列(固定长度滑动窗口:保留最近 N 轮对话!)
history = deque(maxlen=10)
history.append("msg1") # 超过 10 条自动挤掉最老的
datetime —— 时间
from datetime import datetime, timedelta, timezone
now = datetime.now()
utc = datetime.now(timezone.utc)
now.strftime("%Y-%m-%d %H:%M:%S") # 格式化(≈ DateTimeFormatter)
datetime.strptime("2026-09-05", "%Y-%m-%d") # 解析
now + timedelta(days=7) # 偏移
datetime.timestamp(now) # → 秒级时间戳
pathlib —— 现代路径操作(代替 os.path)
from pathlib import Path
p = Path("data") / "logs" / "app.log" # / 拼路径,跨平台
p.parent.mkdir(parents=True, exist_ok=True) # 先建目录
p.write_text("日志内容", encoding="utf-8") # 写文件
p.exists(); p.is_file() # True True
print(p.read_text(encoding="utf-8")) # 一行读完
p.parent; p.stem; p.suffix # data/logs / app / .log
print(list(Path(".").glob("*.py"))) # 通配查找
re —— 正则
import re
re.findall(r"\d+", "订单号123,金额45元") # ['123', '45']
re.sub(r"\s+", " ", "a b c") # 'a b c'
m = re.search(r"(\d{4})-(\d{2})", "2026-09")
m.group(0), m.group(1) # '2026-09', '2026'
enum 与 __slots__(知道即可)
from enum import Enum
class Role(Enum):
SYSTEM = "system"
USER = "user"
ASSISTANT = "assistant"
Role.USER.value # 'user' —— AI 消息角色就适合这么定义
8. 新手必踩的坑
坑 1:可变默认参数(01 章讲过,再强调:这是 Python 面试第一题)
def bad(x, acc=[]): ... # ❌ 所有调用共享同一个 list
def good(x, acc=None): ... # ✅
坑 2:is vs ==
a = [1, 2]; b = [1, 2]
a == b # True —— 值相等
a is b # False —— 不是同一个对象(≈ Java 的 a == b 比引用)
# 小整数缓存陷阱:CPython 缓存了 -5~256
x = 256; y = 256
x is y # True(缓存)
x = 257; y = 257
x is y # False!(PyPy/新版本可能不同)
# 结论:比较值永远用 ==;is 只用于 None / True / False 判断
坑 3:浅拷贝 vs 深拷贝
import copy
original = {"tags": ["a", "b"]}
shallow = original.copy() # dict() / list() / 切片 [:] 都是浅拷贝
shallow["tags"].append("c")
print(original) # {'tags': ['a', 'b', 'c']} ← 内层 list 被共享了!
deep = copy.deepcopy(original) # 深拷贝,彻底独立
调 LLM 时想"留一份原始 messages 备份"再修改?必须
deepcopy(或重新组装),浅拷贝会把你备份一起改了。
坑 4:闭包晚绑定(late binding)
funcs = [lambda: i for i in range(3)]
print([f() for f in funcs]) # [2, 2, 2]!全部捕获同一个 i
# 修复:用默认参数在定义时固化
funcs = [lambda i=i: i for i in range(3)]
print([f() for f in funcs]) # [0, 1, 2]
坑 5:GIL(全局解释器锁)——先知道结论
CPython 有一把全局锁,同一时刻只有一个线程执行 Python 字节码。所以:
- threading 不能加速 CPU 密集任务(Java 转 Python 最容易想错的一点)
- IO 密集(网络请求、读写)线程仍有效(等 IO 时释放锁);更好用的是 asyncio(第 03 章)
- CPU 密集用 multiprocessing 多进程
📌 与时俱进注:Python 3.13 起官方提供实验性的 free-threaded(无 GIL)构建,3.14 继续推进,但 2026 年的今天主流部署仍带 GIL。依据:Python 官方文档 What's New in 3.13 / free-threading 说明。初学阶段按"有 GIL"设计程序不会错。
坑 6:str 拼接性能
循环里 s += x 拼大字符串是 O(n²);用 "".join(parts)。
坑 7:None 传播
def get_user(uid: int):
return None # 模拟"查无此人"的接口
data = get_user(42)
# data["key"] # ⚠️ None["key"] → TypeError 高发!
# 好习惯:先判空再取值
if data and data.get("key"): ...
print("安全通过") # data 为 None 时上面的 if 也安然无恙
9. 自测清单
- [ ]
__init__、__repr__、__eq__分别对应 Java 的什么? - [ ]
@dataclass帮你生成了哪三个方法?frozen=True是什么效果? - [ ]
@timer装饰器等价于哪行赋值语句? - [ ] 生成器和 list 的本质区别是什么?什么场景必须用生成器?
- [ ]
with语句依赖对象的哪两个魔术方法? - [ ]
list[dict[str, Any]]用 Java 泛型怎么写? - [ ]
a == b和a is b的区别?判断 None 用哪个? - [ ] 为什么 threading 加速不了 CPU 密集任务?
- [ ]
deque(maxlen=N)适合 AI 对话里的什么场景?
10. 小练习
练习 1(OOP):用 @dataclass 定义 ChatMessage(role, content, tokens=0),再定义 Conversation 类:内部用 deque(maxlen=20) 存消息,提供 add() / total_tokens() / __repr__() 方法。
练习 2(装饰器):写 @cache 装饰器:函数结果按参数缓存(提示:闭包持有 dict,参数做 key;进一步可用 functools.lru_cache 对照)。
练习 3(生成器):写生成器 fib() 产出无限斐波那契数列,配合 itertools.islice 打印前 20 项。
练习 4(综合):手写 retry(times, delay) 装饰器:失败等待 delay 秒再重试,全部失败抛异常,用 time.sleep + 闭包实现。
11. 参考与出处
以下均为官方一手来源(访问日期:2026-09-05):
| 主题 | 出处 |
|---|---|
| 类与 OOP | Python 官方教程 · 类 |
| dataclass | dataclasses 模块文档 |
| typing | typing 模块文档 |
| 装饰器/闭包 | functools 模块文档、官方 FAQ · 装饰器 |
| 迭代器/生成器 | 官方教程 · 迭代器与生成器 |
| 上下文管理器 | contextlib 文档 |
| collections / datetime / pathlib / re | 标准库目录 |
| 拷贝语义 | copy 模块文档 |
| GIL 与 free-threading | What's New in Python 3.13 |