《Python编程入门》11.2 logging 与结构化日志
print 无法分级、无法定向、无法关闭。本节讲清 logging 的 Logger/Handler/Formatter/Filter 架构、层级传播与 basicConfig 的局限,用 dictConfig 写完整配置,示范 % 延迟格式化、exc_info 异常日志、自定义 JSON Formatter 与日志轮转。
tag
print 无法分级、无法定向、无法关闭。本节讲清 logging 的 Logger/Handler/Formatter/Filter 架构、层级传播与 basicConfig 的局限,用 dictConfig 写完整配置,示范 % 延迟格式化、exc_info 异常日志、自定义 JSON Formatter 与日志轮转。
datetime 与时区是 Python 最易踩坑的标准库之一。本节从时间戳讲起,理清 naive 与 aware 的区别,实测 utcnow() 的弃用警告,用 zoneinfo 演示 Asia/Shanghai 与 America/New_York 的夏令时边界,并对比 replace 与 astimezone 的经典陷阱、ISO 8601 与 timedelta。
json.dumps/loads 的 ensure_ascii、indent、sort_keys、default 参数与中文坑,csv 的 DictReader/DictWriter、newline 的必要性与 Sniffer,3.11+ 只读的 tomllib,以及序列化安全:pickle 为何不能反序列化不可信数据、yaml.safe_load 与 JSON 的边界。
从 open() 的模式与 encoding 参数讲起,说清为什么必须显式指定编码;对比 read()、readlines() 与逐行迭代的内存差异,用 pathlib.Path 做现代路径操作,覆盖 glob/rglob/iterdir/walk、__file__ 定位、UTF-8 BOM 与 GBK 乱码排查,以及 tempfile 配合 os.replace 的原子写入。
字符串方法全景、切片与不可变、三种格式化的取舍与 f-string 调试写法、encode/decode 的编码错误处理,再到 re 模块的 match/search/fullmatch 区别、分组与命名分组、findall 与 finditer、贪婪与非贪婪、sub 回引,以及预编译缓存与一条日志解析实战。
静态类型检查器只能看代码,看不到运行时流入的 JSON、环境变量与用户输入。本节先划清静态类型的边界,再入门 Pydantic V2:用 BaseModel 定义模型、Field 加约束、model_validate / model_dump 序列化、field_validator / model_validator 写校验,并用 TypeAdapter 处理裸类型。
当同一段逻辑要处理多种类型时就需要泛型。本节从 TypeVar 与 bound 讲起,给出泛型函数与泛型类的写法,对照 3.12 的 PEP 695 新语法如何简化 type X = ...、class C[T] 与 def f[T];随后深入 Protocol 子类型、TypedDict、NamedTuple、@overload、Self 与 3.13 的 TypeIs,并讲清变型的直觉。
类型注解是 Python 静态分析的地基,运行时却几乎不做任何检查。本节从「写了 int 传 str 也能跑」讲起,梳理 Optional、Union、内置泛型、Literal、Final、Annotated 等语法,厘清 Any 与 object 的边界,再用 pyright 与 mypy --strict 在真实文件上跑出检查结果,并说明注解在运行时如何被读取、3.14 起为何默认延迟求值。
装饰器本质是函数到函数的高阶函数,@deco 只是 f = deco(f) 的语法糖。本节从最朴素的例子讲起,强调必须用 functools.wraps 保留元信息;再讲带参数的装饰器、类装饰器与实例方法装饰器的差异、缓存装饰器的实现思路与可哈希约束、singledispatch 的类型分派,以及多个装饰器的执行顺序,最后落到鉴权、重试、计时、注册表等真实工程场景。
yield 不只是用来吐出值的。本节从 send() 讲起,说明生成器为什么被称为协程的雏形;再讲 throw() 与 close() 的清理语义、GeneratorExit 为何必须重新抛出、yield from 的委托规则以及它如何取出子生成器的返回值;最后用生成器搭一条读文件、过滤、转换、聚合的惰性管道,说清它比每步生成一个列表省在哪里,并点一句异步生成器为何是另一套东西。
很多人把可迭代对象和迭代器当成同一件事,这恰恰是理解 for 循环与生成器的第一道坎。本节先用一张表把两者的协议差异讲透:可迭代对象只需实现 __iter__,迭代器还要实现 __next__ 并在耗尽时抛出 StopIteration。接着展开 for 循环的等价写法、手写迭代器类、生成器函数与生成器表达式的区别、生成器的惰性与一次性,并实测生成器比列表省下多少内存。
本节讲 Python 的上下文管理器与 with 语句。先说明 with 解决的资源成对获取与释放问题,再拆解上下文管理协议的两个特殊方法、退出方法的三个参数,以及返回真值即吞掉异常的机制;随后用 contextlib 的装饰器把生成器改造成上下文管理器,介绍抑制异常与动态管理多个资源两个工具;最后给出 with 管理多资源时的等价展开,并解释文件、锁、数据库连接为何都该做成上下文管理器。
本节讲如何设计一套可维护的异常体系。先讲自定义异常的写法:继承哪个基类、怎样携带结构化字段、如何重写字符串表示;再讲异常链,用真实回溯区分隐式链与显式链,说明何时该用 from 保留原始错误、何时用 from None 切断噪音;随后给出库作者的异常设计原则,包括定义基类、让调用方能精确捕获、不用异常做正常控制流,以及宽泛捕获后重新抛出的正确姿势;最后介绍警告机制与弃用装饰器。
本节讲 Python 的异常机制。先从异常沿调用栈向上传播说起,梳理基类与普通异常的分层结构,解释为什么不要写裸 except、也不该捕获最顶层的基类异常;再用可运行代码演示四段捕获结构的执行顺序、返回值与清理块的交互,以及多重 except 中子类必须写在父类前面的原因。最后介绍异常组与带星号的分支语法、assert 在优化模式下被整体删除的陷阱,以及异常相对返回值的性能代价。
本节讲清魔术方法如何让自定义对象融入 Python 语法:__str__ 与 __repr__ 的分工、定义 __eq__ 后 __hash__ 被置空的陷阱、用容器协议让对象支持 len 与迭代、__call__ 让实例可调用、上下文管理协议,以及 __add__/__radd__/__iadd__ 运算符重载与 __format__ 配合 f-string 的写法。
本节讲清继承语法与 super() 的含义——它不是父类而是 MRO 里的下一个类,并演示菱形问题。接着介绍 abc.ABC 的约束力与组合优于继承的判断标准,再拆解 dataclass 的 field、default_factory、frozen、order、slots 等参数,用选型表对比 dataclass、NamedTuple 与 TypedDict,并演示 PEP 695 泛型类。
本节从 class 语句做了什么讲起,厘清 __init__ 是初始化器而非构造函数,实例属性与类属性的区别及可变类属性被所有实例共享的陷阱;再讲方法绑定中 self 的来源、property 与 setter、staticmethod 与 classmethod 的取舍,用 __mro__ 说清属性查找顺序与 C3 线性化,最后用 tracemalloc 实测 __slots__ 的内存收益。
本节处理导入体系里最易踩坑的三类问题:循环导入的成因与三种真实修法(调整导入位置、TYPE_CHECKING 类型专用导入、把共享部分抽到第三个模块)、PEP 420 命名空间包的机制与适用场景,以及惰性导入的几种做法,含 Python 3.15 的 PEP 810 语法与版本边界。最后用 python -X importtime 看导入与启动时间的关系。
本节聚焦「包」这种组织模块的方式:有 __init__.py 的目录发生了什么、空文件与写满代码的 __init__.py 各有什么代价、from 包.模块 import 名字 如何逐层解析、相对导入「只能在包内使用」的硬约束从何而来。还讲了 __all__ 对 import * 的控制、用 importlib 动态导入,以及预导入在易用性与启动时间间的取舍。
本节从「Python 是怎么找到并加载模块」出发,讲透 import 的三种形式及差异、模块对象与名字绑定的区别、sys.modules 缓存带来的「模块只执行一次」语义,以及 sys.path 的构成与搜索顺序。重点用真实实验对比 python -m 与直接运行脚本时 sys.path[0] 的不同,解释相对导入为何会报错,并说明 __name__ 守卫的原理。