本工具使用的开源库

本工具代码中捆绑了 1 个开源库。

Python 速查 — 简明参考

现代 Python 3 (3.11+) 语法、内置类型与最常用标准库速查手册,覆盖约 80% 日常场景。

Py

Python Python 3.11 (LTS 风格)

CPython · 多范式(OOP · 函数式 · 过程式) · 动态 · 强类型 · 鸭子类型

学习路径

先学会运行 python3 与 REPL → 掌握变量、内置类型与流程控制 → 深入函数、类与异常 → 用标准库读写文件与网络 → 理解引用与可变性(深浅拷贝)→ 再按需学 asyncio、进程与正则 → 最后熟悉 venv/pip/pyproject 工程化。FAQ 节适合回头避坑。

1.Hello World 与构建环境

运行 Python、交互式 REPL、虚拟环境与包管理。

最小程序

print 输出到控制台。Python 用缩进组织代码块,无花括号分号。

1
2
3
4
5
6
7
8
# hello.py
print('Hello, world!')
# 运行:python3 hello.py
# 或交互式:python3 进入 REPL 后直接输入
print(f'1 + 1 = {1 + 1}')
# 输出到 stderr:
import sys
print('error', file=sys.stderr)

运行方式

python3 执行脚本、-c 执行字符串、-m 运行模块。交互式 REPL 立即求值。

1
2
3
4
5
6
7
8
9
10
11
# 运行脚本:
# python3 hello.py
# 执行字符串:
# python3 -c "print('hi')"
# 运行模块(含 __main__):
# python3 -m http.server 8000
# 检查版本:
# python3 --version
# 进入 REPL:
# python3
# 交互提示符 >>> 输入代码回车即执行

虚拟环境

venv 隔离项目依赖。激活后 pip 安装只作用于当前环境。项目必须用虚拟环境。

1
2
3
4
5
6
7
8
9
10
11
12
# 创建虚拟环境:
python3 -m venv .venv
# 激活(Windows):
# .venv\Scripts\activate
# 激活(macOS/Linux):
# source .venv/bin/activate
# 停用:deactivate
# 激活后:
# python -m pip install requests
# 检查解释器:
# which python # Linux/macOS
# 虚拟环境名出现在提示符前缀

pip 包管理

pip 安装/卸载/冻结依赖。版本用 ==、范围用 >=、>=x,<y。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 安装:
pip install requests
# 指定版本:
pip install 'requests==2.31.0'
# 版本范围:
pip install 'requests>=2,<3'
# 升级:pip install -U requests
# 卸载:pip uninstall requests
# 导出依赖:
pip freeze > requirements.txt
# 从文件安装:
pip install -r requirements.txt
# 列出:pip list
# 升级 pip:python3 -m pip install -U pip

pyproject.toml

现代项目配置文件:依赖、构建系统、工具配置统一放在 pyproject.toml。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# pyproject.toml(PEP 621):
# [project]
# name = "my-app"
# version = "0.1.0"
# requires-python = ">=3.11"
# dependencies = [
# "requests>=2.31",
# "httpx>=0.24",
# ]
#
# [build-system]
# requires = ["setuptools>=68"]
# build-backend = "setuptools.build_meta"
#
# [tool.ruff]
# line-length = 88
# 安装依赖:pip install -e .

Shebang 脚本

#!/usr/bin/env python3 让脚本可执行。POSIX 系统 chmod +x 后直接 ./script.py。

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env python3
# script.py
# 给执行权限:
# chmod +x script.py
# 直接运行:
# ./script.py
print('可执行脚本')
# 不需要依赖具体 python 路径:
# env 负责找到 python3
# Windows 双击 .py 由关联解释器运行
# 无 shebang 时用 python3 script.py 运行

REPL 交互

python3 进入 REPL 交互调试。_ 保存上次结果。exit() 退出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> 1 + 2
3
>>> _
3
>>> def f(x): return x * 2
>>> f(21)
42
# 退出 REPL:
# exit() 或 Ctrl+D
# 内置 help:
>>> help(str)
>>> help('str'.upper)
# 查看对象属性:
>>> dir('hello')
# 历史:上下箭头翻阅

__main__ 与模块

if __name__ == '__main__' 区分直接运行与导入。作为脚本 vs 作为模块的惯用法。

1
2
3
4
5
6
7
8
9
10
11
12
# greetings.py
def hello(name='world'):
return f'Hello, {name}!'
if __name__ == '__main__':
# 仅直接运行时执行
print(hello())
# 直接运行:python3 greetings.py
# 导入使用:
# from greetings import hello
# hello('Nick')
# __name__ 直接运行时是 '__main__',导入时是模块名

2.变量与类型标注

动态类型、类型注解、多重赋值、作用域与海象运算符。

赋值与推断

变量无需声明,赋值即创建。类型动态推断,可随时改变类型。

1
2
3
4
5
6
7
8
9
10
11
x = 42 # 推断 int
y = 3.14 # float
name = 'Rex' # str
ok = True # bool
empty = None # NoneType
# 类型可改变:
x = '现在是字符串'
# 同时多个变量:
a = b = c = 0
# 变量是名字,不是盒子:
# 绑定到对象,重新赋值换绑定

类型注解

PEP 484 类型注解,冒号标注变量类型。注解不影响运行时,只是提示与检查。

1
2
3
4
5
6
7
8
9
10
11
12
count: int = 0
items: list[str] = []
mapping: dict[str, int] = {}
# PEP 604 联合语法(3.10+):
optional: int | None = None
# 函数注解:
def add(a: int, b: int) -> int:
return a + b
# 注解不强制:
add('a', 'b') # 运行时不报错
# 检查工具:mypy / pyright
# 泛型:list[int] 而非 list[int, ...]

多重赋值

a, b = b, a 一行交换。扩展解包用 *rest。解包要求数量匹配。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
a, b, c = 1, 2, 3
# 交换:
a, b = b, a
# 扩展解包:
first, *rest = [1, 2, 3, 4]
# first=1, rest=[2,3,4]
*head, last = [1, 2, 3, 4]
# head=[1,2,3], last=4
# 解包元组:
x, y = (10, 20)
# 解包字典键:
keys = 'ab'
d = dict(zip(keys, [1, 2]))
# 数量不匹配会 ValueError

常量约定

Python 无常量关键字。ALL_CAPS 命名模块级常量是惯例。

1
2
3
4
5
6
7
8
9
10
11
PI = 3.14159
MAX_RETRIES = 3
DATABASE_URL = 'postgres://...'
# 只是约定,仍可修改:
# PI = 99 可以但别这么干
# 作用:
# 一眼看出不会变的值
# 配置集中管理
# 用 Enum 替代魔法数字:
# from enum import Enum
# class Color(Enum): RED = 1

作用域 LEGB

查找顺序 Local → Enclosing → Global → Built-in。函数内赋值需 global/nonlocal 声明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
counter = 0
def inc():
global counter # 读写模块级
counter += 1
def outer():
x = 1
def inner():
nonlocal x # 读写外层函数变量
x += 1
inner()
print(x) # 2
# 只读外层变量无需 nonlocal
# 模块顶部定义全局
# 内置函数(len/print)在 Built-in 层

海象运算符

:= 赋值表达式在表达式中赋值。3.8+ 特性。避免重复计算。

1
2
3
4
5
6
7
8
9
10
11
12
# 常规写法:
line = input('> ')
while line != 'quit':
print(line)
line = input('> ')
# 海象写法:
while (line := input('> ')) != 'quit':
print(line)
# 列表推导里复用:
# [y for x in data if (y := f(x)) > 0]
# 注意括号:赋值表达式必须加括号
# 用途:条件里初始化并判断

None 与布尔

None 表示无值。真值测试用 if x 而非 if x == True。空容器为假。

1
2
3
4
5
6
7
8
9
10
11
12
13
value = None
if value is None: # 判 None 用 is
print('无值')
# 真值判断:
if [1, 2]: pass # 非空列表为真
if []: pass # 空列表为假
# 假值:False/0/''/[]/{}/(,)/None
if not value: # value 为假时
pass
# 三目:
status = 'ok' if value else 'empty'
# 空值合并替代:
name = value or '默认值'

del 与垃圾回收

del 删除变量或容器元素。删除后名字失效,对象引用计数减一。

1
2
3
4
5
6
7
8
9
10
11
x = 42
del x # 删除名字
# x 再访问会 NameError
items = [1, 2, 3]
del items[0] # 删除元素 -> [2, 3]
d = {'a': 1}
del d['a'] # 删除键
# 删除字典键用 pop 可带默认:
val = d.pop('a', None)
# 配合 try 捕获 KeyError
# del 局部变量常用于提前释放大对象

3.数据类型

内置类型:数值、布尔、序列、映射与集合,以及类型注解进阶。

数值类型

int 任意精度、float 浮点、complex 复数。bool 是 int 子类。除法的两种语义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
n = 10 # int
g = 3.14 # float
c = 1 + 2j # complex
big = 2 ** 100 # int 任意精度
# 除法:
5 / 2 # 2.5(真除法)
5 // 2 # 2(整除)
5 % 2 # 1(取余)
# 进制:
0b1010 # 2 进制 10
0o12 # 8 进制 10
0xff # 16 进制 255
# 类型转换:
int('42'); float(1); bool(0)

布尔与 None

True/False 首字母大写。None 是单例。and/or 短路求值返回操作数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
t, f = True, False
# 短路逻辑:
print(0 and 'x') # 0(短路返回左值)
print('' or '默认') # '默认'
print(None or []) # []
# 三元:
age = 17
msg = '成年' if age >= 18 else '未成年'
# 比较链:
if 0 < x < 10: pass
# None 判等:
if x is None: pass
# bool() 转换:
bool([]) # False
bool([1]) # True

序列类型

str/list/tuple 都是序列:支持索引、切片、迭代。str/tuple 不可变。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
s = 'abc'
# 索引:
s[0] # 'a'
s[-1] # 'c'
# 切片:
s[1:] # 'bc'
s[::-1] # 'cba' 反转
# 序列通用:
len(s), min([3,1]), max([3,1])
sum([1,2,3])
list('abc') # ['a','b','c']
# 存在性:
'a' in s # True
# 不可变序列不能改:
# s[0] = 'x' 错误
# 索引越界 IndexError

映射 dict

dict 键值对,保持插入顺序。键必须可哈希。.get 安全取值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
d = {'a': 1, 'b': 2}
# 取值:
d['a'] # 1,缺键 KeyError
d.get('c', 0) # 0,缺键返回默认
# 增改:
d['c'] = 3
d.setdefault('x', 0)
# 删除:
d.pop('a', None)
# 合并:
d.update({'y': 9})
# 视图:
d.keys(); d.values(); d.items()
# 字典推导:
{n: n**2 for n in range(3)}
# 键要求可哈希:数字/字符串/元组可以

集合类型

set 无序去重,frozenset 不可变。并集交集差集运算。

1
2
3
4
5
6
7
8
9
10
11
12
13
s = {1, 2, 3}
s.add(4)
s.remove(2) # 缺键 KeyError
s.discard(99) # 缺键不报错
# 去重:
list(set([1, 1, 2])) # [1, 2]
# 运算:
{1, 2} | {2, 3} # 并集 {1,2,3}
{1, 2} & {2, 3} # 交集 {2}
{1, 2} - {2} # 差集 {1}
# 不可变集合:
frozenset([1, 2])
# 空集合用 set(),{} 是空字典

字节类型

bytes 不可变字节、bytearray 可变。encode/decode 编码转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
b = b'hello' # bytes
ba = bytearray(b'abc')
ba[0] = 120 # 可变
# 编码:
text = '你好'.encode('utf-8')
# b'\xe4\xbd\xa0\xe5\xa5\xbd'
text.decode('utf-8') # '你好'
# 编码错误:
text.decode('utf-8', errors='ignore')
# 字符串与字节互转必带编码
# 二进制数据:
import struct
struct.pack('>i', 42)
# 字节序:> 大端 < 小端

类型注解进阶

typing 模块:Optional/Union/TypeVar。泛型容器标注。3.10 起可用 | 语法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from typing import Optional, Union, TypeVar, Callable
# 可选:
x: Optional[int] = None # 同 int | None
# 联合:
y: Union[int, str] # 同 int | str
# 泛型变量:
T = TypeVar('T')
def first(items: list[T]) -> T:
return items[0]
# 可调用:
def apply(f: Callable[[int], int], n: int) -> int:
return f(n)
# 类型别名(3.12+):
type Vector = list[float]
# 检查:mypy / pyright

鸭子类型

「能走能叫就是鸭子」:关心对象的行为而非类型。协议(协议)定义接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 鸭子类型:只要对象有 len 就能用
def size(obj):
return len(obj)
size([1, 2]) # 2
size('abc') # 3
# 协议(typing.Protocol):
from typing import Protocol
class Sizeable(Protocol):
def __len__(self) -> int: ...
def total_len(objs: list[Sizeable]) -> int:
return sum(len(o) for o in objs)
# 结构子类型:形状匹配即可
# 无需显式继承

4.引用与可变性

Python 的对象引用语义:可变/不可变、深浅拷贝与共享陷阱。

引用语义

变量绑定到对象而非容器。多个名字可指向同一对象。赋值是绑定不是拷贝。

1
2
3
4
5
6
7
8
9
10
11
12
13
a = [1, 2, 3]
b = a # b 和 a 指向同一列表
b.append(4)
print(a) # [1, 2, 3, 4]
# 重新绑定不影响原对象:
a = [9]
print(b) # 仍是 [1,2,3,4]
# 整数等不可变对象:
x = 10
y = x
x = 20
print(y) # 10,值没变
# 关键:分清「改对象」与「改绑定」

可变与不可变

可变类型可原地改:list/dict/set。不可变:int/str/tuple/frozenset。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 可变:原地修改
lst = [1, 2]
lst.append(3) # 同一对象变化
# 不可变:操作产生新对象
s = 'ab'
s2 = s.upper() # 新字符串
# s 不变
# 不可变值可做字典键:
d = {('a', 1): 'x'}
# 列表不可做键:
# {[1]: 'x'} 错误
# 元组含可变元素也不可哈希
# 不可变对象可安全共享

浅拷贝

copy.copy 浅拷贝:外层新对象,内层元素仍共享引用。列表切片也是浅拷贝。

1
2
3
4
5
6
7
8
9
10
11
12
13
import copy
original = [[1, 2], [3, 4]]
shallow = copy.copy(original)
# 外层不同:
shallow is original # False
# 内层共享:
shallow[0] is original[0] # True
# 列表切片同:
sub = original[:]
# 修改内层会互相影响:
shallow[0].append(99)
print(original) # [[1,2,99], [3,4]]
# 浅拷贝适合一层结构

深拷贝

copy.deepcopy 递归复制所有层。嵌套可变结构修改互不影响。注意开销。

1
2
3
4
5
6
7
8
9
10
11
12
import copy
original = [[1, 2], [3, 4]]
deep = copy.deepcopy(original)
deep[0].append(99)
print(original) # [[1, 2], [3, 4]]
print(deep) # [[1, 2, 99], [3, 4]]
# 深拷贝递归复制:
# 所有嵌套对象都是新的
# 开销与深度成正比
# 有循环引用也能处理
# 自引用对象:copy.deepcopy(x)
# 大数据慎用,考虑结构设计

共享引用陷阱

多个引用共享同一可变对象时一处修改处处可见。参数传递也是传引用。

1
2
3
4
5
6
7
8
9
10
11
# 函数内修改传入列表:
def add_item(items):
items.append('x') # 影响调用方!
my_list = []
add_item(my_list)
print(my_list) # ['x']
# 想避免就地改就传副本:
add_item(my_list.copy())
# 或者函数内返回新列表
# 默认参数陷阱见 FAQ
# 判断:函数是否承诺只读参数

is 与 == 的区别

== 比较值,is 比较身份(是否同一对象)。None 判等用 is None。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
a = [1, 2]
b = [1, 2]
a == b # True,值相等
a is b # False,不同对象
# 小整数驻留:
x = 256
y = 256
x is y # True(-5..256 缓存)
# 大整数:
x, y = 10**6, 10**6
x is y # 不一定,别依赖
# 正确用法:
if x is None: pass
# 比较值用 ==:
if x == 42: pass

默认参数陷阱

可变默认参数在定义时求值并共享。用 None 哨兵 + 函数内创建。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# BAD:共享默认列表
def add(x, acc=[]):
acc.append(x)
return acc
add(1) # [1]
add(2) # [1, 2] !
# GOOD:None + 函数内创建
def add(x, acc=None):
if acc is None:
acc = []
acc.append(x)
return acc
# 默认值只求值一次:
# 定义时创建,之后复用

驻留与缓存

小整数与短字符串驻留(interning)优化内存。别依赖 is 比较内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 小整数缓存范围:
# -5 到 256 是单例
x, y = 256, 256
x is y # True
# 超出范围:
a, b = 257, 257
a is b # 不确定(实现相关)
# 字符串驻留:
'hello' is 'hello' # True(编译期常量)
# 运行时生成不同:
'he' + 'llo' is 'hello' # 可能 False
# 结论:
# 内容比较一律用 ==
# is 只用于 None 与单例

5.流程控制

条件分支、循环、模式匹配与推导式。

if / elif / else

缩进分层。elif 多个条件。表达式可以是任意布尔判定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
score = 85
if score >= 90:
grade = 'A'
elif score >= 80:
grade = 'B'
elif score >= 70:
grade = 'C'
else:
grade = 'D'
# 单行写法(不推荐复杂):
if x > 0: print('正数')
# 多条件:
if a and not b: pass
if x in (1, 2, 3): pass

模式匹配

match-case 结构化模式匹配(3.10+)。按形状匹配并解构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def handle(command):
match command.split():
case ['quit']:
print('退出')
case ['open', path]:
print(f'打开 {path}')
case ['open', path, *rest]:
print(f'打开 {path} 附加 {rest}')
case _:
print('未知命令')
# 守卫:
def f(point):
match point:
case (0, 0): print('原点')
case (x, y) if x == y: print('对角')
case (x, y): print(f'({x}, {y})')
# 默认分支 _ 兜底

for 循环

for 遍历可迭代对象。enumerate 带索引、zip 并行、dict.items 遍历键值。

1
2
3
4
5
6
7
8
9
10
11
12
for i in range(5):
print(i) # 0..4
for idx, item in enumerate(['a', 'b']):
print(idx, item)
for k, v in {'a': 1}.items():
print(k, v)
for a, b in zip([1, 2], ['x', 'y']):
print(a, b) # (1,'x') (2,'y')
# range 步长:
range(0, 10, 2) # 0 2 4 6 8
# 倒序:reversed(lst)
# 反向带索引:range(len(x)-1, -1, -1)

while 循环

while 条件为真时循环。配合 break 提前退出。防止死循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
n = 0
while n < 5:
print(n)
n += 1
# 无限循环 + break:
while True:
line = input('> ')
if line == 'quit':
break
print(line)
# while-else:
# 未 break 退出时执行 else
attempts = 0
while attempts < 3:
if try_connect():
break
attempts += 1
else:
print('连接失败')

break 与 continue

break 退出整个循环,continue 跳过本轮。else 子句在未 break 时执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for i in range(10):
if i % 2 == 0:
continue # 跳过偶数
if i > 7:
break # 提前结束
print(i) # 1 3 5 7
# for-else:
for n in range(2, 10):
for d in range(2, n):
if n % d == 0:
break
else:
print(n, '是质数')
# else 在正常走完循环时执行
# 注意:else 属于 for 而非 if

推导式

列表/字典/集合推导式一行生成。条件过滤 + 表达式转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 列表推导式:
squares = [n ** 2 for n in range(5)]
# [0, 1, 4, 9, 16]
# 带条件:
evens = [n for n in range(10) if n % 2 == 0]
# 字典推导式:
{n: n * 2 for n in range(3)}
# {0:0, 1:2, 2:4}
# 集合推导式:
{n % 3 for n in range(10)}
# 嵌套:
flat = [y for xs in matrix for y in xs]
# 生成器表达式(惰性):
total = sum(n * n for n in range(100))

真值判断

if x 直接判定真值。0/''/[]/None 为假。避免与 True/False 显式比较。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 空容器判断:
items = []
if not items: # GOOD:空则假
print('空列表')
# BAD:别这么写
if len(items) == 0: pass
# None 判断:
if value is not None:
print(value)
# 数字判断:
if count: pass # 0 为假
# 字符串:
if name: pass # '' 为假
# 显式布尔:
if bool(x): pass
# 优先级:可读性优先,条件别太长

遍历技巧

itertools 高效迭代组合。groupby 分组、chain 拼接、product 笛卡尔积。

1
2
3
4
5
6
7
8
9
10
11
12
from itertools import chain, groupby, product
# 拼接:
list(chain([1, 2], [3, 4])) # [1,2,3,4]
# 笛卡尔积:
list(product('ab', [1, 2]))
# [('a',1),('a',2),('b',1),('b',2)]
# 分组(需排序):
rows = sorted(rows, key=lambda r: r['type'])
for key, group in groupby(rows, key=lambda r: r['type']):
print(key, list(group))
# 无限迭代:itertools.count()
# 排列组合:itertools.permutations / combinations

6.函数

函数定义、参数传递、装饰器、生成器与闭包。

函数定义

def 定义函数,缩进体。return 返回值,无 return 返回 None。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def greet(name):
return f'Hello, {name}!'
print(greet('Nick'))
# 无返回值:
def log(msg):
print(msg) # 返回 None
# 多返回值(元组):
def min_max(nums):
return min(nums), max(nums)
lo, hi = min_max([3, 1, 4])
# 类型注解:
def add(a: int, b: int) -> int:
return a + b
# 文档字符串:
def f():
"""描述功能"""
pass

参数类型

位置参数、关键字参数、默认值。调用可混合位置与关键字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def show(a, b, c):
print(a, b, c)
# 位置调用:
show(1, 2, 3)
# 关键字调用:
show(c=3, a=1, b=2)
# 混合(位置在前):
show(1, c=3, b=2)
# 默认参数:
def greet(name, greeting='Hi'):
return f'{greeting}, {name}!'
# 默认参数放最后:
# def f(a=1, b) 错误
# 只读关键字参数:
def f(*, strict=False): pass
# 只读位置参数(3.8+):
def f(a, /): pass

*args 与 **kwargs

*args 收集多余位置参数为元组,**kwargs 收集多余关键字为字典。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def log_all(*args, **kwargs):
print('位置:', args)
print('关键字:', kwargs)
log_all(1, 2, x=3, y=4)
# 转发调用:
def wrapper(*args, **kwargs):
return original(*args, **kwargs)
# 解包调用:
items = [1, 2, 3]
print(*items) # 1 2 3
config = {'a': 1}
f(**config)
# 顺序:位置 -> *args -> 关键字 -> **kwargs
# 不要滥用,参数过多建议用对象

匿名函数

lambda 单表达式匿名函数。用于 key、map/filter 等高阶场景。

1
2
3
4
5
6
7
8
9
10
11
square = lambda x: x * x
square(5) # 25
# 排序 key:
users.sort(key=lambda u: u['age'])
# 高阶函数:
list(map(lambda x: x * 2, [1, 2]))
list(filter(lambda x: x > 1, [1, 2]))
# 立即调用:
(lambda x: x + 1)(41) # 42
# 复杂逻辑用 def,lambda 只写简单表达式
# lambda 不能包含语句(if/return)

闭包

内部函数捕获外部变量。返回函数记住外层作用域。工厂函数常用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def make_multiplier(n):
def multiply(x):
return x * n # 捕获 n
return multiply
double = make_multiplier(2)
triple = make_multiplier(3)
double(5) # 10
triple(5) # 15
# 闭包修改外层变量:
def counter():
count = 0
def inc():
nonlocal count
count += 1
return count
return inc
# 每个闭包独立保存自己的 n/count

装饰器

@decorator 包装函数。不修改原函数代码而增强行为。计时/日志常用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import functools, time
def timing(func):
@functools.wraps(func) # 保留元信息
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
print(f'{func.__name__}: {time.perf_counter()-start:.4f}s')
return result
return wrapper
@timing
def slow():
time.sleep(0.1)
# 带参数装饰器:
def repeat(times):
def deco(func):
@functools.wraps(func)
def wrapper(*a, **kw):
for _ in range(times): func(*a, **kw)
return wrapper
return deco
# 内置装饰器:@staticmethod/@classmethod/@property

生成器

yield 惰性产出值。生成器函数逐项产出,内存友好。大序列处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def count_up(n):
i = 0
while i < n:
yield i # 暂停返回
i += 1
for x in count_up(3):
print(x) # 0 1 2
# 生成器表达式:
squares = (n * n for n in range(3))
# 无限序列:
def fib():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# 惰性:不全部进内存
# 一次性:耗尽后需重建
# 管道:链式生成器处理流

函数式工具

map/filter/reduce/sorted 函数式处理。functools 工具:partial 偏函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from functools import partial, reduce
# map 转换:
doubled = list(map(lambda x: x * 2, [1, 2, 3]))
# filter 过滤:
evens = list(filter(lambda x: x % 2 == 0, range(6)))
# reduce 聚合:
total = reduce(lambda a, b: a + b, [1, 2, 3])
# 偏函数:固定参数
def power(base, exp): return base ** exp
square = partial(power, exp=2)
square(5) # 25
# 生成器可接 map/filter:
list(map(str, range(3))) # ['0','1','2']
# 现代风格推荐推导式,可读性更好

递归

函数调用自身。必须有基准情形。深度限制约 1000(sys.setrecursionlimit)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)
factorial(5) # 120
# 递归深度限制:
import sys
sys.getrecursionlimit() # 默认 1000
# 深递归用迭代或栈:
def fib_iter(n):
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return a
# 树遍历/分治自然用递归
# 记忆化:@functools.lru_cache

7.字符串

str 不可变、f-string 格式化、常用方法与编码。

字符串基础

单/双/三引号创建。转义序列。str 不可变,操作返回新字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
s1 = '单引号'
s2 = "双引号"
s3 = '''多行
字符串'''
# 原始字符串(路径/正则):
r'C:\\path'
# 转义:
'\n' # 换行
'\t' # 制表
'\\' # 反斜杠
# 字符串是序列:
s = 'hello'
s[0] # 'h'
s[1:4] # 'ell'
# 不可变:
# s[0] = 'H' 错误
# 拼接:
first + last
' '.join(['a', 'b'])

常用方法

upper/lower、strip 去除空白、split 分割、join 拼接、startswith 判断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
s = ' Hello World '
s.upper() # ' HELLO WORLD '
s.lower()
s.strip() # 去首尾空白
s.startswith('He') # True
s.endswith('ld') # True
s.replace('World', 'Python')
# 查找:
s.find('World') # 索引或 -1
s.index('World') # 无则 ValueError
s.count('l')
# 分割拼接:
'1,2,3'.split(',') # ['1','2','3']
'-'.join(['a', 'b']) # 'a-b'
# 判断:
'abc'.isalpha(); '123'.isdigit()

f-string 格式化

f"{var}" 插值表达式。格式说明符:对齐、精度、千分位。3.12+ 支持引号复用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
name = 'Nick'
age = 30
f'{name} 今年 {age} 岁'
# 表达式:
f'{age + 1}'
# 对齐宽度:
f'{name:>10}' # 右对齐 10
f'{name:<10}' # 左对齐
# 数字格式:
f'{3.14159:.2f}' # '3.14'
f'{1000000:,}' # '1,000,000'
f'{0.5:.0%}' # '50%'
# 进制:
f'{255:x}' # 'ff'
# 日期:
import datetime
f'{datetime.date.today():%Y-%m-%d}'
# 字典取值:f'{d["key"]}'

format 与 % 格式化

str.format 位置/命名占位。% 运算符旧式格式化。f-string 更推荐。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# format 方法:
'{0} {1}'.format('a', 'b')
'{name} 今年 {age}'.format(name='Nick', age=30)
# 对齐:
'{:>10}'.format('hi') # 右对齐
'{:.2f}'.format(3.14159)
# 旧式 % 格式化:
'%s 今年 %d' % ('Nick', 30)
'%.2f' % 3.14159
'%x' % 255
# 三种风格对照:
# f-string:f'{x:.2f}'(3.6+ 推荐)
# format:'{:.2f}'.format(x)
# 旧式:'%.2f' % x
# 新代码用 f-string

分割与拼接

split 按分隔符拆分、rsplit 从右拆、partition 三分。join 高效拼接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
'a,b,c'.split(',') # ['a','b','c']
'a,b,c'.split(',', 1) # ['a', 'b,c']
'a b c'.split() # 空白自动分割
'a-b-c'.rsplit('-', 1) # ['a-b', 'c']
'key=value'.partition('=') # ('key','=','value')
# 拼接(性能):
parts = ['a', 'b', 'c']
', '.join(parts)
# 逐字符拆分:
list('abc')
# 多分隔符:
import re
re.split(r'[;,|]', 'a;b,c|d')
# 避免 + 循环拼接(O(n^2))

去除与填充

strip 去首尾、lstrip/rstrip 单侧。zfill 补零、center 居中、removeprefix。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
s = ' hello '
s.strip() # 'hello'
s.lstrip() # 'hello '
s.rstrip() # ' hello'
' x \n'.strip() # 'x'
# 指定字符:
'xxhelloxx'.strip('x') # 'hello'
# 填充:
'42'.zfill(5) # '00042'
'hi'.center(7, '-') # '--hi---'
# 前缀移除(3.9+):
'/usr/bin'.removeprefix('/usr/') # 'bin'
'/usr/bin'.removesuffix('/bin') # '/usr'
# 换行清理:
text.rstrip('\n')

Unicode 与编码

Python str 是 Unicode 码点序列。encode 转字节、decode 还原。len 计数点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
s = '你好👋'
len(s) # 3(emoji 是 1 个码点)
# 编码:
b = s.encode('utf-8')
# 解码:
b.decode('utf-8')
# 码点:
ord('中') # 20013
chr(20013) # '中'
# 规范化:
import unicodedata
unicodedata.normalize('NFKC', s)
# 大小写:
s.casefold() # 更严格的小写
# 编码错误处理:
b.decode('utf-8', errors='replace')
# 遍历码点:for ch in s 已是码点级

查找与替换

find/index 查找、replace 替换、translate 映射替换。count 计数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
s = 'the quick brown fox'
s.find('quick') # 4
s.find('z') # -1
s.index('quick') # 4(无则异常)
s.rfind('o') # 从右找
s.count('o') # 4
# 替换:
s.replace('fox', 'dog')
s.replace('o', '0', 1) # 只换 1 次
# 替换多个(translate):
table = str.maketrans({'a': 'A', 'e': 'E'})
'apple'.translate(table) # 'ApplE'
# 大小写不敏感替换:
import re
re.sub('(?i)the', 'THE', s)
# 提取子串:s[4:9]

文本包装

textwrap 换行排版:wrap 按宽度断行、dedent 去缩进、fill 填充。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import textwrap
long_text = '这是一个比较长的文本,需要按照指定宽度断行显示。'
# 按宽度断行:
lines = textwrap.wrap(long_text, width=20)
# 填充成段落:
textwrap.fill(long_text, width=20)
# 去公共缩进:
block = '''\
line1
line2'''
textwrap.dedent(block)
# 缩进:
textwrap.indent('a\nb', ' ')
# 用途:CLI 帮助文本排版
# 控制台输出对齐

8.集合与容器

list/dict/set/tuple 操作、collections 模块与排序。

列表操作

append 尾部、insert 插入、remove 删除、pop 弹出、index 查找、count 计数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
nums = [3, 1, 2]
nums.append(4) # [3,1,2,4]
nums.insert(0, 0) # [0,3,1,2,4]
nums.remove(1) # 删除首个 1
last = nums.pop() # 弹出尾部
first = nums.pop(0) # 弹出指定位置
nums.index(2) # 索引
nums.count(1)
nums.reverse() # 原地反转
nums.clear() # 清空
# 复制:
nums[:] 或 nums.copy()
# 扩展:
nums.extend([5, 6])
nums += [7, 8]

字典操作

取值 .get、合并 update/|、遍历 items、排序 items、嵌套访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
d = {'a': 1, 'b': 2}
d['c'] = 3 # 新增/更新
d.get('x', 0) # 缺省 0
val = d.setdefault('y', 0)
# 合并(3.9+):
merged = d | {'z': 9}
d.update({'z': 9})
# 遍历:
for k, v in d.items(): pass
# 按键排序:
sorted(d.items())
# 按值排序:
sorted(d.items(), key=lambda kv: kv[1])
# 嵌套:
d['user']['name']
# 安全访问链:
d.get('user', {}).get('name')
# 默认字典:collections.defaultdict

集合运算

并集 |、交集 &、差集 -、对称差 ^。成员判断 O(1)。去重。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
a = {1, 2, 3}
b = {2, 3, 4}
a | b # {1,2,3,4} 并集
a & b # {2,3} 交集
a - b # {1} 差集
b - a # {4}
a ^ b # {1,4} 对称差
# 判断:
2 in a # True O(1)
# 子集:
a <= b; a < b
# 去重保序:
list(dict.fromkeys([3, 1, 3, 2]))
# 找重复元素:
[x for x in lst if lst.count(x) > 1]
# 不可变:frozenset

元组用法

tuple 不可变序列。多返回值、记录、字典键。命名元组 namedtuple。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
t = (1, 2, 3)
# 解包:
a, b, c = t
# 单元素需逗号:
single = (1,)
# 记录用途:
point = (10, 20)
x, y = point
# 命名元组:
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(1, 2)
p.x; p[0] # 两种访问
# 命名元组有类型注解版本:
# from typing import NamedTuple
# 不可变:t[0] = 9 错误
# 用作字典键(可哈希)

collections 工具

Counter 计数、deque 双端队列、defaultdict 默认值、OrderedDict 有序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from collections import Counter, deque, defaultdict
# 计数:
Counter('abracadabra').most_common(2)
# [('a', 5), ('r', 2)]
# 双端队列:
q = deque([1, 2])
q.appendleft(0); q.pop(); q.popleft()
# 固定长度(丢弃旧元素):
last5 = deque(maxlen=5)
# 默认字典:
dd = defaultdict(list)
dd['k'].append(1) # 自动创建空 list
# 有序字典:
# Python 3.7+ dict 本身有序
# 计数器运算:
Counter('aab') + Counter('abb')
# Counter('aab') - Counter('b')

排序

sorted 返回新列表,list.sort 原地。key 指定排序键。reverse 倒序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
nums = [3, 1, 2]
sorted(nums) # [1,2,3] 新列表
nums.sort() # 原地排序
# 倒序:
sorted(nums, reverse=True)
# 按 key 排序:
words = ['banana', 'apple', 'cherry']
sorted(words, key=len)
# 字典列表:
users.sort(key=lambda u: u['age'])
# 多级排序:
sorted(users, key=lambda u: (u['age'], u['name']))
# 稳定排序:
sorted(users, key=lambda u: u['age'])
# 降序键:
sorted(users, key=lambda u: -u['age'])

切片

seq[start:stop:step] 切片。负索引从尾数。反转、步长、复制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
s = list(range(10))
s[2:5] # [2, 3, 4]
s[:3] # 从头
s[7:] # 到尾
s[-3:] # 末尾 3 个
s[::2] # 每隔一个
s[::-1] # 反转
s[1:8:2] # 步长
# 赋值:
s[1:3] = [9] # 替换片段
# 删除:
del s[1:3]
# 复制:
copy = s[:]
# 字符串同理:
'hello'[::-1] # 'olleh'
# 注意切片越界不报错

堆与队列

heapq 堆排序、queue 线程安全队列。优先级处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import heapq
nums = [3, 1, 4, 1, 5]
heapq.heapify(nums) # 建最小堆
heapq.heappop(nums) # 弹出最小
heapq.heappush(nums, 0)
# 取最大/最小 n 个:
heapq.nlargest(2, [3, 1, 4])
heapq.nsmallest(2, [3, 1, 4])
# 优先级队列:
import queue
pq = queue.PriorityQueue()
pq.put((1, '低'))
pq.put((0, '高'))
pq.get() # (0, '高')
# 线程安全:queue.Queue()
# 任务队列:queue.Queue(maxsize)

9.内存与性能

垃圾回收、引用计数、内存工具与性能优化。

垃圾回收

引用计数为主 + 分代 GC 处理循环引用。无需手动释放。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 对象引用计数归零即回收
x = [1, 2]
y = x
del x # 计数减一,仍存活
del y # 计数归零,回收
# 循环引用:
import gc
gc.collect() # 手动触发
# 对象销毁钩子:
class Temp:
def __del__(self):
print('回收')
# 一般无需手动 gc
# 引用计数无法处理循环引用,分代 GC 兜底

memoryview 与零拷贝

memoryview 视图访问缓冲区,避免拷贝。大二进制数据处理高效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
data = bytearray(b'hello world')
view = memoryview(data)
view[0] # 104
view[6:11] # b'world'
# 切片是视图不是拷贝:
sub = view[6:11]
sub[0] = 87 # 修改影响原数据
# 格式:
mem = memoryview(b'\x00\x01')
mem.cast('H') # 按 16 位无符号读
# 用途:
# 1. 大文件分片处理
# 2. 协议解析避免复制
# 3. struct 组合视图
# 性能敏感时比切片拷贝快

弱引用

weakref.ref 不阻止回收。缓存大对象、避免循环引用泄漏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import weakref
class Node:
pass
node = Node()
ref = weakref.ref(node)
print(ref()) # 对象
node = None # 释放强引用
gc.collect()
print(ref()) # None(已回收)
# WeakKeyDictionary/WeakValueDictionary:
# 键/值弱引用,回收自动移除
cache = weakref.WeakValueDictionary()
# 循环引用预防:
# 用弱引用替代某一条边
# 用途:缓存、观察者、单例

__slots__ 省内存

__slots__ 声明固定属性,避免实例 dict。大量小对象省内存提速。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 默认每个实例有 __dict__:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
# 用 __slots__:
class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(1, 2)
# p.z = 3 错误:未声明属性
# 节省:每实例少一个 dict
# 百万级小对象效果显著
# 缺点:不能动态加属性、少 __dict__

大数据处理

大数据分块处理、惰性迭代、避免一次性载入。流式读文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 大文件逐行读:
with open('big.log') as f:
for line in f: # 惰性逐行
process(line)
# 大列表用生成器:
def read_all_lines(path):
with open(path) as f:
for line in f:
yield line
# 分批处理:
def chunks(iterable, size):
from itertools import islice
it = iter(iterable)
while batch := list(islice(it, size)):
yield batch
for batch in chunks(range(100), 10):
process_batch(batch)
# 避免 list(map(...)) 全量载入
# numpy 处理大规模数值更高效

性能分析

cProfile 分析耗时、timeit 微基准。找瓶颈再优化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import timeit, cProfile
# 微基准:
timeit.timeit("'x' * 100", number=10000)
# 或命令行:
# python3 -m timeit "'-'.join(str(n) for n in range(100))"
# 全量分析:
# python3 -m cProfile -s cumulative script.py
# 代码内分析:
cProfile.run('my_func()', sort='cumulative')
# 计时装饰器:
import time
def timed(f):
def w(*a, **k):
t = time.perf_counter()
r = f(*a, **k)
print(f.__name__, time.perf_counter() - t)
return r
return w
# 原则:先测再优化

性能优化技巧

局部变量快于全局、推导式快于循环、集合查找 O(1)。避免重复计算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 局部变量缓存:
import math
def f(n):
m = math.sqrt # 局部引用
return m(n)
# 集合成员 O(1):
allowed = set(['a', 'b'])
if x in allowed: pass
# 推导式 vs 循环:
# 通常推导式更快更简洁
squares = [n * n for n in range(1000)]
# 避免重复计算:
# 循环外提公因子
# 字符串拼接用 join
# 大数值用 numpy
# 复杂热路径考虑 C 扩展 / numba

内存常见错误

MemoryError 内存不足、栈溢出 RecursionError、参考循环。诊断工具。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 栈溢出:
def infinite():
return infinite()
# RecursionError: maximum recursion depth
# 内存不足:
# MemoryError(大数据一次性载入)
# 诊断:
import tracemalloc
tracemalloc.start()
# 运行代码...
snapshot = tracemalloc.take_snapshot()
top = snapshot.statistics('lineno')
print(top[:5])
# 对象引用图:
import objgraph # 第三方
# objgraph.show_refs([obj])
# 修复:
# 分块处理、释放引用、用生成器

10.类与对象

class 定义、继承、魔术方法、dataclass 与 enum。

类基础

class 定义类型。__init__ 构造初始化。self 指实例。方法绑定到实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def greet(self):
return f'Hi, {self.name}'
p = Person('Nick', 30)
p.greet()
# 类属性(共享):
class Counter:
total = 0 # 类级
def __init__(self):
Counter.total += 1
# 实例属性(独有):
p.name
# 动态属性:
p.email = '[email protected]'
# 类型检查:isinstance(p, Person)

属性访问

实例属性优先于类属性。setattr/getattr/hasattr 动态访问。property 属性描述。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class User:
default_role = 'user' # 类属性
def __init__(self, name):
self.name = name
# 属性查找:实例 -> 类 -> 基类
u = User('a')
u.default_role # 'user'
# 动态访问:
hasattr(u, 'name') # True
getattr(u, 'name', None)
setattr(u, 'role', 'admin')
# property 属性:
class Circle:
def __init__(self, r):
self._r = r
@property
def area(self):
return 3.14 * self._r ** 2
c = Circle(2)
c.area # 方法当属性访问
# property setter 做校验

三种方法

实例方法 self、类方法 cls、静态方法。@staticmethod/@classmethod 装饰。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Tool:
count = 0
def __init__(self, name):
self.name = name
Tool.count += 1
def instance_method(self):
return self.name # 访问实例
@classmethod
def class_method(cls):
return cls.count # 访问类
@staticmethod
def helper(x):
return x * 2 # 无 self/cls
# 调用:
t = Tool('锤子')
t.instance_method()
Tool.class_method()
Tool.helper(4)
# 类方法用于工厂/计数器
# 静态方法用于工具函数

继承

子类继承父类方法。super() 调父类。方法覆盖。多继承注意 MRO。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return f'{self.name} 发出声音'
class Dog(Animal):
def speak(self):
return f'{self.name} 汪汪'
def fetch(self):
return f'{self.name} 捡球'
d = Dog('Rex')
d.speak() # 覆盖
# super() 调用父类:
class Cat(Animal):
def __init__(self, name, color):
super().__init__(name)
self.color = color
# isinstance 与 issubclass
# 多继承按 MRO 顺序查找
# 鸭子类型:少用继承多用协议

魔术方法

双下划线方法定制行为:__repr__ 显示、__eq__ 比较、__len__ 长度、__add__ 加法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Vector:
def __init__(self, x, y):
self.x, self.y = x, y
def __repr__(self):
return f'Vector({self.x}, {self.y})'
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __eq__(self, other):
return (self.x, self.y) == (other.x, other.y)
def __len__(self):
return 2
v = Vector(1, 2)
str(v); v + v; v == Vector(1, 2)
# 常用:
# __str__ 面向用户
# __getitem__ 下标访问
# __call__ 对象可调用
# __bool__ 真值判断
# __enter__/__exit__ 上下文管理器

dataclass

@dataclass 自动生成 __init__/__repr__/__eq__。声明式数据类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from dataclasses import dataclass, field
@dataclass
class Point:
x: int
y: int
label: str = 'origin' # 默认值
tags: list = field(default_factory=list)
p = Point(1, 2)
p2 = Point(1, 2)
p == p2 # True(自动 __eq__)
repr(p) # Point(x=1, y=2, label='origin')
# 冻结(不可变):
@dataclass(frozen=True)
class Config:
debug: bool = False
# 排序:
@dataclass(order=True)
class Item:
price: int
# 转换字典:
from dataclasses import asdict, astuple
asdict(p)
# 继承字段也处理

Enum 枚举

Enum 定义命名常量集。str 枚举、自动编号、遍历成员。替代魔法数字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from enum import Enum, auto
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
class Status(Enum):
ACTIVE = auto() # 自动编号
INACTIVE = auto()
# 访问:
Color.RED
Color(1) # 按值查找
Color.RED.value # 1
Color.RED.name # 'RED'
# 遍历:
for c in Color: pass
# 字符串枚举:
class Mode(str, Enum):
FAST = 'fast'
SLOW = 'slow'
# 比较:
Color.RED is Color.RED # True 单例
# 描述:
class Kind(Enum):
A = ('a', '描述')
def __init__(self, code, desc):
self.code = code
self.desc = desc

上下文管理器

with 管理资源。__enter__/__exit__ 定制。contextlib.contextmanager 简化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Timer:
def __enter__(self):
import time
self.start = time.perf_counter()
return self
def __exit__(self, exc_type, exc, tb):
import time
print(f'耗时 {time.perf_counter()-self.start:.4f}s')
return False # False 不吞异常
with Timer():
pass
# 简化版:
from contextlib import contextmanager
@contextmanager
def timer():
import time
t = time.perf_counter()
yield
print(f'耗时 {time.perf_counter()-t:.4f}s')
with timer():
pass
# with open(...) 是内置上下文管理器
# contextlib.suppress / ExitStack

11.异常处理

try-except、异常层级、自定义异常、with 资源管理。

try / except

捕获异常处理错误。except 捕获特定类型。as e 取异常对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
try:
num = int('abc')
except ValueError:
print('无法转换')
# 捕获并访问异常:
except ValueError as e:
print(e)
# 多类型:
except (ValueError, TypeError):
pass
# 顺序:更具体的类型在前
try:
result = risky()
except (ValueError, KeyError) as e:
print(f'可预期错误: {e}')
except Exception as e:
print(f'未知错误: {e}')
# 不捕获会向上传播直至崩溃

else 与 finally

else 在无异常时执行。finally 无论成败都执行(清理资源)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
try:
data = parse(input_data)
except ValueError as e:
print(f'解析失败: {e}')
else:
print('解析成功') # 无异常才执行
finally:
cleanup() # 总是执行
# 组合使用:
# try 放可能出错代码
# else 放成功才做逻辑
# finally 放清理(关连接/释放)
# finally 不阻止异常传播
# 可省略 else 或 finally

异常层级

Exception 是所有错误基类。常见:ValueError/TypeError/KeyError/IndexError。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
try:
d['missing'] # KeyError
except KeyError:
pass
try:
[1][5] # IndexError
except IndexError:
pass
try:
int('x') # ValueError
except ValueError:
pass
try:
1 / 0 # ZeroDivisionError
except ZeroDivisionError:
pass
# 层级:
# BaseException > Exception > 具体错误
# KeyboardInterrupt/SystemExit 不是 Exception
# except Exception 捕获业务错误
# 别裸 except:(连退出都吞)

自定义异常

继承 Exception 定义业务异常。带字段与消息。调用方按类型捕获。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class ValidationError(Exception):
pass
class NotFoundError(Exception):
def __init__(self, entity, key):
self.entity = entity
self.key = key
super().__init__(f'{entity} 不存在: {key}')
# 抛出:
def get_user(user_id):
if user_id == 0:
raise NotFoundError('用户', user_id)
return {'id': user_id}
# 捕获:
try:
get_user(0)
except NotFoundError as e:
print(e.entity, e.key)
# 基类统一处理:
class AppError(Exception): pass
# 派生 SpecificError(AppError)

raise 与链式

raise 主动抛出。raise ... from ... 链接原因。try 内 raise 重新抛出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def validate(age):
if age < 0:
raise ValueError('年龄不能为负')
# 重新抛出:
try:
risky()
except SomeError:
raise # 原样继续抛
# 异常链(保留原因):
try:
parse_file(path)
except FileNotFoundError as e:
raise RuntimeError(f'读取失败: {path}') from e
# 抑制链:
raise NewError('x') from None
# 链显示:Traceback (most recent call last):
# 原因在前,当前在后
# 调试时保留 __cause__

断言

assert 检查不变量。调试期验证。生产可被 -O 关闭,别用于校验用户输入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def process(data):
assert data is not None, 'data 不能为空'
assert isinstance(data, list), '期望列表'
return len(data)
# 断言失败抛 AssertionError
# 禁用断言运行:
# python3 -O script.py
# 用途:
# 1. 内部不变量
# 2. 调试期参数检查
# 3. 类型前置条件
# 不要用 assert 做:
# 用户输入校验
# 安全边界检查
# 关键业务逻辑

异常与日志

logging 记录异常带堆栈。logger.exception 在 except 内记录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import logging
logging.basicConfig(level=logging.INFO)
try:
x = 1 / 0
except ZeroDivisionError:
logging.exception('除法出错') # 带堆栈
# 或:
logger.error('出错', exc_info=True)
# 级别:
logging.debug/info/warning/error
# 配置:
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s %(levelname)s %(message)s',
)
# 结构化:
logger.info('请求', extra={'id': req_id})
# 别用 print 打日志(无级别无时间戳)

错误处理模式

EAFP 请求宽恕优于许可 vs LBYL 先看后做。守卫子句、早返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# EAFP:直接尝试,出错捕获
def parse_eafp(s):
try:
return int(s)
except ValueError:
return None
# LBYL:先检查再操作
def parse_lbyl(s):
if not s.isdigit():
return None
return int(s)
# 通用捕获 vs 精确捕获:
# 精确:except ValueError
# 兜底:except Exception
# 守卫子句提前返回:
def handle(user):
if user is None:
return
if not user.active:
return
process(user)
# 减少嵌套,逻辑扁平

12.输入输出

print/input、文件读写、pathlib、JSON/CSV 与标准流。

输出与输入

print 多参数、sep/end 分隔。input 读一行字符串。格式化输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
print('a', 'b') # a b
print('a', 'b', sep=', ') # a, b
print('end', end='!\n')
# 输出到文件:
with open('out.txt', 'w') as f:
print('text', file=f)
# 输入:
name = input('你的名字? ') # 总是 str
# 数字输入:
age = int(input('年龄? '))
# 一次多输入:
a, b = input().split()
# 输出变量:
print(f'Hi {name}')
# 控制台进度:
print('\r进度 50%', end='')
# 缓冲刷新:print(..., flush=True)

文件读写

open 模式:r 读/w 写/a 追加/b 二进制。read/readline/readlines。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 读:
f = open('data.txt', encoding='utf-8')
text = f.read() # 全部
line = f.readline() # 一行
lines = f.readlines() # 行列表
f.close()
# 写:
f = open('out.txt', 'w', encoding='utf-8')
f.write('内容\n')
f.writelines(['a', 'b'])
f.close()
# 模式:
# 'r' 读 'w' 覆盖写 'a' 追加
# 'rb'/'wb' 二进制
# 'r+' 读写
# 务必用 with 自动关闭

with 与文件

with 自动关闭文件,异常时也关闭。读文件惯用法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 推荐写法:
with open('data.txt', encoding='utf-8') as f:
text = f.read() # 用完自动关闭
# 逐行:
with open('data.txt', encoding='utf-8') as f:
for line in f:
print(line.rstrip())
# 写:
with open('out.txt', 'w', encoding='utf-8') as f:
f.write('hello')
# 追加:
with open('log.txt', 'a') as f:
f.write('更多')
# 错误处理:
try:
with open('missing.txt') as f:
pass
except FileNotFoundError:
print('文件不存在')
# 编码参数必带,跨平台一致

pathlib 路径

Path 对象跨平台路径:/, exists、mkdir、read_text。替代 os.path。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pathlib import Path
p = Path('data') / 'sub' / 'file.txt'
p.exists() # 是否存在
p.is_file(); p.is_dir()
p.mkdir(parents=True, exist_ok=True)
# 读写:
text = Path('data.txt').read_text(encoding='utf-8')
Path('out.txt').write_text('hi', encoding='utf-8')
# 遍历:
for f in Path('.').glob('*.py'):
print(f.name)
# rglob 递归:
Path('.').rglob('*.json')
# 属性:
p.name; p.suffix; p.stem
p.parent; p.absolute()
# 常用目录:
Path.home()
Path.cwd()
# 拼写简单、跨平台统一

JSON 读写

json.loads/dumps 序列化。dict/list 与 JSON 互转。ensure_ascii=False 保留中文。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import json
data = {'name': 'Nick', 'tags': ['a', 'b']}
# 序列化:
text = json.dumps(data, ensure_ascii=False)
# 美化:
json.dumps(data, indent=2, ensure_ascii=False)
# 反序列化:
obj = json.loads(text)
# 文件读写:
with open('data.json', 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
with open('data.json', encoding='utf-8') as f:
obj = json.load(f)
# 错误:
try:
json.loads('invalid')
except json.JSONDecodeError as e:
print(e)
# 类型:JSON 对象 <-> dict

CSV 读写

csv 模块处理逗号分隔。writer 写、reader/DictReader 读。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import csv
rows = [['name', 'age'], ['Nick', 30], ['Anna', 25]]
# 写:
with open('data.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerows(rows)
# 读:
with open('data.csv', newline='', encoding='utf-8') as f:
reader = csv.reader(f)
for row in reader:
print(row)
# 字典读写:
with open('data.csv', encoding='utf-8') as f:
for row in csv.DictReader(f):
print(row['name'])
# 写入字典:
with open('o.csv', 'w', newline='') as f:
w = csv.DictWriter(f, fieldnames=['name'])
w.writeheader()
w.writerow({'name': 'x'})
# 注意引号/换行处理

标准流

sys.stdin/stdout/stderr。管道数据。逐行处理 stdin。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import sys
# 读一行:
line = sys.stdin.readline()
# 逐行读全部(管道):
for line in sys.stdin:
sys.stdout.write(line.upper())
# 错误流:
print('error', file=sys.stderr)
# 管道用法:
# cat data.txt | python3 filter.py
# 输出:
# python3 filter.py > out.txt
# 编码:
# PYTHONIOENCODING=utf-8 控制流编码
# input() 等价 sys.stdin.readline()
# 进度输出到 stderr 避免污染 stdout

二进制读写

二进制模式读写字节。struct 打包解析、随机访问 seek/tell。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 读写字节:
with open('img.bin', 'rb') as f:
data = f.read()
with open('out.bin', 'wb') as f:
f.write(b'\x00\x01\x02')
# 固定大小分块:
with open('file.bin', 'rb') as f:
while chunk := f.read(1024):
process(chunk)
# struct 打包:
import struct
packed = struct.pack('>I', 1024) # 4 字节
value = struct.unpack('>I', packed)[0]
# 随机访问:
with open('db.bin', 'rb') as f:
f.seek(100) # 跳到偏移
data = f.read(10)
# 末尾追加:'ab' 模式

13.常见误区

Python 日常开发最容易踩的坑与正确写法。

可变默认参数

可变默认值在定义时创建并共享,多次调用累积。用 None 哨兵。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# BAD:共享默认列表
def add(item, basket=[]):
basket.append(item)
return basket
add('a') # ['a']
add('b') # ['a', 'b'] !
# GOOD:None + 函数内创建
def add(item, basket=None):
if basket is None:
basket = []
basket.append(item)
return basket
# 原因:默认值定义时求值一次
# 影响:list/dict/set 全中招

is 与 == 混淆

is 比身份,== 比值。小整数/短字符串可能驻留,别依赖 is 判断内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
# BAD:用 is 比内容
a = [1, 2]
b = [1, 2]
a is b # False,但值相同
# GOOD:== 比较值
a == b # True
# 判断 None 用 is:
x = None
x is None # True
# 小心驻留:
# 257 is 257 在 CPython 可能 True
# 但这是实现细节,勿依赖
# 规则:内容用 ==,单例/None 用 is

循环闭包陷阱

循环中 lambda 捕获延迟绑定变量。立即执行需绑定当前值。

1
2
3
4
5
6
7
8
9
10
11
# BAD:都打印 9
funcs = [lambda: i for i in range(10)]
funcs[0]() # 9
# GOOD:绑定当前值
funcs = [lambda i=i: i for i in range(10)]
funcs[0]() # 0
# 或使用默认参数捕获:
funcs = [(lambda x: lambda: x)(i) for i in range(10)]
# 原因:闭包捕获变量名而非值
# 修正:默认参数在定义时求值
# 注意:for 循环变量在结束后仍存在

字符串拼接

循环 + 拼接 O(n^2) 慢。join 或列表收集更高效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# BAD:循环拼接
s = ''
for i in range(10000):
s += str(i) # 每次创建新字符串
# GOOD:收集后 join
parts = []
for i in range(10000):
parts.append(str(i))
s = ''.join(parts)
# 或生成器:
s = ''.join(str(i) for i in range(10000))
# 少量拼接 + 没问题:
name = first + ' ' + last
# 规则:多次循环拼接用 join
# f-string 适合已知片段

循环中修改列表

遍历时增删列表导致跳过/越界。遍历副本或重建列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# BAD:删除元素跳过
items = [1, 2, 3, 4]
for x in items:
if x % 2 == 0:
items.remove(x) # 索引错乱
# GOOD:重建新列表
items = [x for x in items if x % 2 == 1]
# 或遍历副本:
for x in items[:]:
if x % 2 == 0:
items.remove(x)
# 遍历 dict 时修改键同样危险
# 原则:迭代器失效 -> 迭代副本
# 或改为构建新容器

内置名遮蔽

用内置名作变量遮蔽原函数。list/str/input/sum 别覆盖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# BAD:遮蔽内置
def total(data):
sum = 0 # 覆盖内置 sum
for x in data:
sum += x
return sum
# 后续再用 sum() 会报错
# GOOD:用别的名字
total = 0
def total(data):
return sum(data)
# 常见陷阱:
# list = [] 遮蔽 list()
# input = ... 遮蔽 input()
# type/str/dict 同理
# 列表推导式变量不泄漏(3+ 独立作用域)

吞异常

裸 except 吞掉所有异常。至少记录或重新抛出。精确捕获具体类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# BAD:裸 except 静默吞掉
import os
try:
os.remove('f.txt')
except:
pass # 什么都不知道了
try:
value = int(x)
except Exception:
value = 0 # 掩盖类型错误
try:
risky()
except Exception:
print('出错了') # 连什么错都不清楚
# GOOD:精确捕获 + 记录
import logging
try:
value = int(x)
except (ValueError, TypeError) as e:
logging.warning('转换失败 %s', e)
value = 0
# 别吞 KeyboardInterrupt/SystemExit

全局变量误用

函数内赋值全局变量产生 Local 而非修改全局。需 global 声明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# BAD:看似修改全局实际报错
count = 0
def inc():
count += 1 # UnboundLocalError
# 原因:+= 是赋值,创建局部 count
# GOOD:声明 global
def inc():
global count
count += 1
# 读全局无需声明:
def show():
print(count)
# 避免滥用全局:
# 用参数传递 + 返回值
# 或用类封装状态
# 配置对象模块级只读

拷贝与排序

sort 原地改列表,sorted 返回新列表。引用共享导致误改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# BAD:sort 原地改
original = [3, 1, 2]
sorted_list = original.sort() # 返回 None
# 原列表也被排序
# GOOD:sorted 返回新列表
original = [3, 1, 2]
sorted_list = sorted(original)
# 或 copy 再 sort:
c = original[:]; c.sort()
# 引用共享:
b = original
b.append(9) # original 也变
# 需要独立用拷贝:
new_list = original[:]
# 函数内改参数影响调用方:
# 用 copy 传参

14.并发与异步

threading、asyncio、GIL 与并发的选择。

线程基础

threading.Thread 创建线程。start 启动、join 等待、daemon 守护。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import threading
def work(n):
print(f'线程 {n} 工作')
threads = []
for i in range(3):
t = threading.Thread(target=work, args=(i,))
threads.append(t)
t.start()
for t in threads:
t.join() # 等待完成
# 线程对象带参数:
threading.Thread(target=work, args=(1,), daemon=True)
# daemon 线程随主线程退出
# 线程数别太多,避免上下文切换开销
# 简单并发场景够用

线程类

继承 Thread 覆写 run。线程安全用锁。封装任务线程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import threading
class Worker(threading.Thread):
def __init__(self, name):
super().__init__(name=name)
self.result = None
def run(self):
self.result = heavy_compute()
w = Worker('job-1')
w.start()
w.join()
print(w.result)
# 不要覆写 start,覆写 run
# 数据经实例属性返回
# 异常在子线程静默:
# run 里 try-except 记录
# 需返回值优先 ThreadPoolExecutor

锁与同步

Lock 保护共享资源。with lock 自动释放。RLock 可重入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import threading
lock = threading.Lock()
counter = 0
def increment():
global counter
for _ in range(1000):
with lock: # 自动获取释放
counter += 1
threads = [threading.Thread(target=increment) for _ in range(10)]
for t in threads: t.start()
for t in threads: t.join()
print(counter) # 10000(有锁保护)
# RLock:同一线程可重复获取
# 条件变量:Condition
# 事件:Event
# 信号量:Semaphore 控制并发数
# 死锁:锁顺序不一致、等待持锁
# 优先用 Queue 传递数据免锁

GIL 与并行

GIL 让 CPython 线程不能真正并行 CPU 密集任务。I/O 密集线程有效。

1
2
3
4
5
6
7
8
9
10
11
12
13
# GIL:全局解释器锁
# 同一时刻只有一个线程执行字节码
# CPU 密集任务多线程无加速:
# 计算任务线程反而更慢
# I/O 密集任务线程有效:
# 等待网络/磁盘时让出 GIL
# 真正并行 CPU 密集:
# 1. multiprocessing 多进程
# 2. C 扩展释放 GIL(numpy 等)
# 3. asyncio 单线程异步
# 判断:
# 瓶颈在 I/O -> 线程/asyncio
# 瓶颈在 CPU -> 进程

多进程

multiprocessing 进程池并行 CPU 密集。ProcessPoolExecutor 封装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from multiprocessing import Pool
def square(n):
return n * n
with Pool(4) as pool:
results = pool.map(square, range(10))
# ProcessPoolExecutor:
from concurrent.futures import ProcessPoolExecutor
with ProcessPoolExecutor(max_workers=4) as ex:
results = list(ex.map(square, range(10)))
# 独立内存:进程间不共享
# 通信:Queue/Pipe
# 启动开销大于线程
# 大数据注意序列化开销
# 配合 if __name__ == '__main__'

asyncio 基础

async def 协程、await 挂起、asyncio.run 运行。事件循环调度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import asyncio
async def say_hi():
await asyncio.sleep(1)
print('hi')
asyncio.run(say_hi())
# await 挂起当前协程让出控制权
# 并发执行:
async def main():
task = asyncio.create_task(say_hi())
await task
asyncio.run(main())
# 事件循环:
# 同一线程内调度协程
# 阻塞代码会卡住循环:
# time.sleep -> await asyncio.sleep
# 阻塞库换异步版:httpx/aiohttp
# 3.11+ TaskGroup 管理任务

async / await

async 定义协程。await 等待结果。并行 gather。超时 asyncio.timeout。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import asyncio
async def fetch(url):
await asyncio.sleep(0.1)
return f'data from {url}'
# 串行:
async def main():
a = await fetch('a')
b = await fetch('b')
# 并行:
results = await asyncio.gather(
fetch('a'), fetch('b'), fetch('c'))
# 超时:
try:
result = await asyncio.wait_for(fetch('a'), timeout=1)
except asyncio.TimeoutError:
print('超时')
# 批量创建任务:
tasks = [fetch(u) for u in urls]
await asyncio.gather(*tasks)
# 返回按传入顺序,异常传播

异步 I/O

aiohttp/httpx 异步请求。异步文件/流。IO 密集吞吐高。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# httpx 异步客户端:
# pip install httpx
# import httpx
async def main():
async with httpx.AsyncClient() as client:
r = await client.get('https://example.com')
return r.status_code
# 同步代码混合:
async def worker():
loop = asyncio.get_event_loop()
result = await loop.run_in_executor(None, blocking_fn)
# 异步文件:
import aiofiles
# async with aiofiles.open('f', 'r') as f:
# text = await f.read()
# 大量并发 I/O 吞吐提升明显
# 数据库:asyncpg/aiomysql

执行器

ThreadPoolExecutor 线程池、ProcessPoolExecutor 进程池。统一 submit/map。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from concurrent.futures import ThreadPoolExecutor, as_completed
def work(n):
return n * 2
with ThreadPoolExecutor(max_workers=4) as ex:
futures = [ex.submit(work, i) for i in range(10)]
for f in as_completed(futures):
print(f.result()) # 完成顺序
# map 按顺序:
with ThreadPoolExecutor(4) as ex:
results = list(ex.map(work, range(10)))
# 异常:
# f.result() 抛回原始异常
# shutdown(wait=True) 等待
# 线程池适合 I/O 密集
# 进程池适合 CPU 密集
# 3.9+ 取消:f.cancel()

15.网络与模块

模块系统、HTTP 请求、URL 处理与服务器。

模块与导入

import 加载模块。from x import y 导入特定名字。包是含 __init__.py 的目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 导入:
import os
import os.path
import json as js # 别名
from datetime import datetime, timedelta
from collections import * # 不推荐
# 相对导入(包内):
# from . import utils
# from ..other import x
# 导入机制:
# sys.path 搜索路径
# 缓存:同模块只加载一次
# __main__ 入口
# 自定义模块:
# utils.py 在同目录
# import utils
# 延迟导入:函数内 import 减少启动时间

HTTP 请求

requests 库 GET/POST、参数、请求头、JSON。响应状态判断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests
# GET:
r = requests.get('https://api.example.com/users',
params={'page': 2}, timeout=5)
# 状态:
r.status_code == 200
r.ok
# 数据:
r.json() # dict
r.text # 文本
r.content # 字节
# POST:
resp = requests.post('https://api.example.com/login',
json={'user': 'nick'},
headers={'Authorization': 'Bearer x'},
timeout=10)
# 错误:
try:
r.raise_for_status()
except requests.HTTPError:
print('请求失败')
# 会话(保持 cookie):
s = requests.Session()

urllib 标准库

urllib.request 标准库请求(无第三方)。urllib.parse URL 解析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import urllib.request
with urllib.request.urlopen('https://example.com') as resp:
data = resp.read()
print(resp.status)
# 编码参数:
req = urllib.request.Request('https://api.com',
headers={'User-Agent': 'my-app'},
data=b'body')
with urllib.request.urlopen(req) as resp:
print(resp.read().decode('utf-8'))
# URL 解析:
from urllib.parse import urlparse, urlencode, quote
urlparse('https://a.com/p?q=1#f')
# ParseResult(scheme='https', netloc='a.com', path='/p', query='q=1')
urlencode({'a': 1, 'b': 'x y'}) # 'a=1&b=x+y'
quote('中文') # 百分号编码
# 简洁请求优先 requests,标准库 urllib 兜底

简易 HTTP 服务

http.server 静态文件/临时服务。BaseHTTPRequestHandler 自定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 静态文件服务:
# python3 -m http.server 8000
# 浏览 http://localhost:8000
# 绑定地址:
# python3 -m http.server 8000 --bind 0.0.0.0
# 自定义处理:
from http.server import HTTPServer, BaseHTTPRequestHandler
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
body = b'{"ok": true}'
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.send_header('Content-Length', str(len(body)))
self.end_headers()
self.wfile.write(body)
def log_message(self, fmt, *args):
pass # 静默
HTTPServer(('', 8000), Handler).serve_forever()
# 生产用框架:FastAPI/Flask

Socket 网络

socket 底层 TCP/UDP。套接字连接、发送接收。抓包协议分析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import socket
# TCP 客户端:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(('example.com', 80))
s.sendall(b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
data = s.recv(4096)
print(data[:200])
# TCP 服务端:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
server.bind(('0.0.0.0', 9000))
server.listen(5)
conn, addr = server.accept()
with conn:
data = conn.recv(1024)
conn.sendall(b'pong')
# UDP:SOCK_DGRAM,sendto/recvfrom
# 超时:s.settimeout(3)
# 复杂协议优先框架

URL 与参数

urlparse 解析、urlencode 构造查询、quote 编码。路径拼接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from urllib.parse import urlparse, urlencode, quote, urljoin
# 解析:
p = urlparse('https://[email protected]:8080/path?q=1#sec')
p.scheme; p.netloc; p.path
p.query; p.fragment
# 构造查询串:
params = urlencode({'q': 'python 教程', 'page': 2})
# 'q=python+%E6%95%99%E7%A8%8B&page=2'
url = f'https://api.com/search?{params}'
# 拼接:
urljoin('https://a.com/docs/', '../about')
# 解码:
from urllib.parse import unquote
unquote('%E4%B8%AD') # '中'
# 路径安全:
quote('a/b', safe='') # 转义斜杠
# 解析参数:
from urllib.parse import parse_qs
parse_qs('a=1&a=2&b=3') # {'a': ['1','2'], 'b': ['3']}

配置与环境

os.environ 环境变量、dotenv 加载 .env、配置分层。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import os
# 读环境变量:
port = int(os.environ.get('PORT', '3000'))
secret = os.environ['SECRET_KEY'] # 缺失 KeyError
# 设置:
os.environ['DEBUG'] = '1'
# 判断:
if os.environ.get('DEBUG') == '1':
print('调试模式')
# dotenv:
# pip install python-dotenv
from dotenv import load_dotenv
load_dotenv() # 加载 .env
# .env 文件:
# PORT=3000
# SECRET_KEY=xxx
# 用途:
# 部署配置与代码分离
# 密钥不进版本库
# 配置校验:缺失直接报错
# 分层:default/dev/prod

API 调用模式

请求封装、重试、限流、错误处理。类型化响应(pydantic)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import requests
from time import sleep
def api_get(url, retries=3):
for attempt in range(retries):
try:
r = requests.get(url, timeout=10)
if r.status_code == 429: # 限流
sleep(2 ** attempt) # 退避
continue
r.raise_for_status()
return r.json()
except requests.RequestException:
if attempt == retries - 1:
raise
sleep(2 ** attempt)
# 统一请求头:
HEADERS = {'User-Agent': 'my-app/1.0'}
# 响应校验(pydantic):
# pip install pydantic
# from pydantic import BaseModel
# class User(BaseModel):
# name: str
# age: int
# u = User.model_validate(r.json())
# 缓存:lru_cache / diskcache

16.时间与日期

datetime、格式化、时间差与时区。

datetime 基础

datetime 构造、date/time 子类、访问年月日、比较运算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from datetime import datetime, date, time
# 当前时间:
now = datetime.now() # 本地
now_utc = datetime.utcnow() # 弃用,用 timezone
# 构造:
d = datetime(2026, 8, 2, 12, 30, 0)
# 访问:
d.year; d.month; d.day
d.hour; d.minute; d.second
# 星期:
d.weekday() # 0=周一
d.isoweekday() # 1=周一
# 仅日期/时间:
date(2026, 8, 2)
time(12, 30)
# 比较:
d > datetime(2026, 1, 1)
# 构造错误月份抛 ValueError

格式化与解析

strftime 格式输出、strptime 解析。%Y 年、%m 月、%d 日、%H 时。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from datetime import datetime
now = datetime.now()
# 格式化:
now.strftime('%Y-%m-%d') # '2026-08-02'
now.strftime('%Y-%m-%d %H:%M:%S')
now.strftime('%H:%M') # 时分
now.strftime('%A') # 星期英文
# 解析字符串:
d = datetime.strptime('2026-08-02', '%Y-%m-%d')
d = datetime.fromisoformat('2026-08-02T12:00:00')
# 常用指令:
# %Y 四位年 %y 两位年
# %m 月 %d 日 %H 24时 %I 12时
# %M 分 %S 秒 %f 微秒
# 中文星期:自定义映射
# 时区安全:先带 tz 再格式化

时间差

timedelta 时间差计算。加减天数/小时。datediff 差值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from datetime import datetime, timedelta
now = datetime.now()
# 加 7 天:
now + timedelta(days=7)
# 加 2 小时:
now + timedelta(hours=2)
# 差 30 分钟:
now - timedelta(minutes=30)
# 计算差值:
d1 = datetime(2026, 8, 2)
d2 = datetime(2026, 1, 1)
delta = d1 - d2
delta.days # 213
delta.total_seconds() # 秒
# 相对参数:
# weeks/days/hours/minutes/seconds
# 时间差可比较:
if delta > timedelta(days=100):
print('超过百天')
# 月差需要自己算:
# timedelta 无月份单位

时区

timezone 偏移、zoneinfo 命名时区(3.9+)。存储 UTC 显示本地。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from datetime import datetime, timezone, timedelta
# 固定偏移:
utc8 = timezone(timedelta(hours=8))
now_utc8 = datetime.now(utc8)
# 命名时区(3.9+):
from zoneinfo import ZoneInfo
shanghai = ZoneInfo('Asia/Shanghai')
now_sh = datetime.now(shanghai)
# 转换:
utc_time = datetime.now(timezone.utc)
local_time = utc_time.astimezone(shanghai)
# 时区感知 vs naive:
# 带 tzinfo 才是感知型
# 比较需同一时区或都感知
# 实践:
# 数据库存 UTC
# 展示时转用户时区
# tzdata 包提供时区库

时间戳

timestamp 转 unix 秒、fromtimestamp 还原。时间戳比较存储。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import time
from datetime import datetime, timezone
# unix 时间戳(秒):
now_ts = time.time()
datetime.now().timestamp()
# datetime 转时间戳:
dt = datetime(2026, 8, 2, tzinfo=timezone.utc)
dt.timestamp()
# 时间戳转 datetime:
datetime.fromtimestamp(now_ts) # 本地
datetime.fromtimestamp(now_ts, timezone.utc) # UTC
# 毫秒:
int(time.time() * 1000)
# 时间戳比较:
if now_ts > last_ts: pass
# 存储:
# unix 秒整数是通用标准
# 展示:从时间戳转本地格式化
# 精度:float 秒含小数

定时与等待

time.sleep 等待、time.perf_counter 计时、schedule 定时任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import time
# 等待:
time.sleep(1.5) # 秒
# 计时:
start = time.perf_counter()
# 运行代码...
elapsed = time.perf_counter() - start
print(f'{elapsed:.4f}s')
# 精度:
time.time() # 墙上时间,可调整
time.perf_counter() # 高精度单调
# 定时循环:
def run_every(interval, fn):
while True:
fn()
time.sleep(interval)
# 定时任务库:
# pip install schedule
# import schedule
# schedule.every(10).minutes.do(job)
# while True: schedule.run_pending()
# 阻塞 vs 异步等待:
# 事件循环里用 asyncio.sleep

日期实用

星期判断、月初月末、相对日期、日期范围。calendar 模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from datetime import datetime, timedelta
import calendar
d = datetime.now()
# 星期判断:
d.weekday() < 5 # 工作日
# 月初:
d.replace(day=1)
# 月末:
last_day = calendar.monthrange(d.year, d.month)[1]
d.replace(day=last_day)
# 日期序列:
start = datetime(2026, 8, 1)
for i in range(7):
day = start + timedelta(days=i)
print(day.strftime('%m-%d %a'))
# 自然语言解析:
# pip install python-dateutil
# from dateutil.parser import parse
# parse('2026-08-02 10:30')
# 月差计算:
# dateutil.relativedelta
# from dateutil.relativedelta import relativedelta
# d + relativedelta(months=1)

时间区间判断

判断时间在区间内、区间重叠、剩余时间。调度与限时任务常用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from datetime import datetime, timedelta
# 区间判断:
now = datetime.now()
start = datetime(2026, 1, 1)
end = datetime(2027, 1, 1)
if start <= now < end:
print('在窗口内')
# 区间重叠:
def overlaps(a1, a2, b1, b2):
return a1 < b2 and b1 < a2
# 剩余时间:
dealine = now + timedelta(hours=2)
remaining = deadline - now
if remaining > timedelta(minutes=30):
print('时间充足')
# 定时窗口:
def in_working_hours(dt):
return 9 <= dt.hour < 18
# 边界处理:
# 半开区间 [start, end) 避免重复
# 跨天注意归一化
# 限速:对比上次时间戳

17.进程与系统

subprocess、sys/os 模块、命令行参数与信号。

subprocess

subprocess.run 执行外部命令。捕获输出、检查返回码、超时。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import subprocess
# 执行并捕获输出:
result = subprocess.run(
['ls', '-l'],
capture_output=True, text=True)
print(result.returncode) # 0 成功
print(result.stdout)
print(result.stderr)
# 检查失败:
result.check_returncode() # 非 0 抛 CalledProcessError
# 超时:
subprocess.run(['sleep', '10'], timeout=2)
# 出错:TimeoutExpired
# 安全:
# 传参数列表,不拼 shell 字符串
# shell=True 有注入风险
# 实时输出:subprocess.Popen 逐行读

sys 模块

sys.argv 参数、sys.exit 退出、sys.path 模块路径、标准流对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import sys
# 命令行参数:
sys.argv # ['script.py', 'arg1', 'arg2']
# 退出:
sys.exit(0) # 成功
sys.exit(1) # 失败
# 模块搜索路径:
sys.path # 列表
sys.path.append('/custom') # 添加
# 版本:
sys.version_info # (3, 11, ...)
sys.version
# 流:
sys.stdin; sys.stdout; sys.stderr
# 平台:
sys.platform # 'win32' / 'darwin' / 'linux'
# 编码:
sys.getdefaultencoding() # 'utf-8'
# 递归限制:
sys.getrecursionlimit()
sys.setrecursionlimit(5000)

os 模块

os 环境、路径、目录操作。os.getcwd 当前目录、os.mkdir 建目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import os
# 环境变量:
os.environ['HOME']
# 目录:
os.getcwd() # 当前目录
os.chdir('/tmp') # 切换
os.mkdir('dir') # 建目录
os.makedirs('a/b/c', exist_ok=True)
# 文件:
os.remove('file')
os.rename('a', 'b')
# 路径(os.path):
os.path.join('a', 'b') # 跨平台拼接
os.path.exists('f')
os.path.isdir('d')
os.path.dirname('/a/b/c') # '/a/b'
os.path.basename('/a/b/c') # 'c'
os.path.splitext('f.txt') # ('f', '.txt')
# 遍历:
for root, dirs, files in os.walk('.'):
print(root, files)
# 推荐 pathlib 替代 os.path

命令行参数解析

argparse 解析参数、帮助、默认值。命令式 CLI 工具。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import argparse
parser = argparse.ArgumentParser(description='示例工具')
parser.add_argument('input', help='输入文件')
parser.add_argument('-v', '--verbose', action='store_true')
parser.add_argument('-n', '--count', type=int, default=1)
args = parser.parse_args()
print(args.input, args.verbose, args.count)
# 使用:
# python3 tool.py data.txt -v -n 3
# 帮助:
# python3 tool.py --help
# 选项:
parser.add_argument('--out', default='out.txt')
# 互斥参数:
# parser.add_mutually_exclusive_group()
# 子命令:
# subparsers = parser.add_subparsers()
# 类型校验:type=int 自动转换

退出码

0 成功,非 0 失败。脚本在 CI/管道中靠退出码判断。异常默认退出 1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import sys
# 正常退出:
sys.exit(0)
# 失败:
sys.exit(1)
sys.exit('错误信息') # 打印到 stderr,退出 1
# 未捕获异常自动退出码 1
# 自定义码:
sys.exit(2) # 用法错误约定
# 返回码判断:
# 命令行:echo $? (POSIX)
# python3 script.py; echo $?
# 调用方捕获:
import subprocess
r = subprocess.run(['python3', 'x.py'])
if r.returncode != 0:
print('失败')
# CI 非 0 即构建失败

文件与进程

大文件处理、文件锁、临时文件、原子写入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 临时文件:
import tempfile, os
with tempfile.NamedTemporaryFile(mode='w', delete=True) as f:
f.write('data')
name = f.name
# 临时目录:
with tempfile.TemporaryDirectory() as d:
print(d) # 用完自动清理
# 原子写入(先写临时再改名):
import os
def atomic_write(path, content):
tmp = path + '.tmp'
with open(tmp, 'w') as f:
f.write(content)
os.replace(tmp, path) # 原子替换
# 文件锁:
# pip install filelock
# from filelock import FileLock
# with FileLock('app.lock'):
# 互斥操作
# 大文件分块读:逐行/固定块

信号处理

signal 模块处理 Ctrl+C/SIGTERM。优雅关闭。POSIX 特性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import signal, sys, time
# 优雅退出标志:
running = True
def handle_stop(signum, frame):
global running
print(f'收到信号 {signum},正在退出...')
running = False
signal.signal(signal.SIGINT, handle_stop) # Ctrl+C
signal.signal(signal.SIGTERM, handle_stop) # kill
# 主循环:
while running:
work()
time.sleep(1)
print('清理完成')
# 忽略信号:
signal.signal(signal.SIGPIPE, signal.SIG_IGN)
# 超时信号(POSIX):
# signal.alarm(5) 5 秒后 SIGALRM
# Windows 仅支持部分信号
# 优雅关闭:保存状态、关闭连接

守护与后台

后台运行、日志轮转、进程守护。supervisor/systemd 托管。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 后台运行:
# python3 script.py &
# 输出重定向:
# python3 script.py > out.log 2>&1 &
# nohup 忽略挂断:
# nohup python3 script.py &
# 日志轮转:
import logging
from logging.handlers import RotatingFileHandler
handler = RotatingFileHandler(
'app.log', maxBytes=1_000_000, backupCount=5)
logging.basicConfig(handlers=[handler])
logging.info('运行中')
# 生产托管:
# systemd unit / supervisor 重启策略
# 检查单实例:
# 锁文件 pid 判断
# 资源限制:ulimit

18.正则与文本

re 模块匹配、搜索、替换、分组与编译。

match 与 search

re.match 从头匹配、re.search 全文搜索。返回 Match 对象或 None。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import re
# search:任意位置
m = re.search(r'\d+', '订单号 123')
if m:
print(m.group()) # '123'
# match:从头开始
m2 = re.match(r'\d+', '123abc')
# m2.group() == '123'
# 匹配失败返回 None:
if re.search(r'xyz', 'abc'): pass
# 位置信息:
m.start(); m.end(); m.span()
# 边界:
re.search(r'\bcat\b', 'a cat eats')
# 元字符需转义:
r'\.' 匹配点号

findall 与 finditer

findall 返回所有匹配列表。finditer 返回迭代器逐个处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
import re
# findall:所有匹配
nums = re.findall(r'\d+', 'a1b22c333')
# ['1', '22', '333']
# 分组时返回元组:
re.findall(r'(\d+)-(\d+)', '1-2,3-4')
# [('1','2'), ('3','4')]
# finditer:迭代 Match
for m in re.finditer(r'\d+', 'a1b22'):
print(m.group(), m.span())
# 需要位置信息用 finditer
# 大文本 finditer 省内存
# 无匹配返回空列表/空迭代

sub 替换

re.sub 正则替换。替换串可用 \1 引用分组。函数式替换处理逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import re
# 简单替换:
re.sub(r'\s+', ' ', 'a b c') # 'a b c'
# 分组引用:
re.sub(r'(\d{4})-(\d{2})-(\d{2})', r'\3/\2/\1',
'2026-08-02')
# '02/08/2026'
# 函数式替换:
re.sub(r'\d+', lambda m: str(int(m.group()) * 2),
'1 and 2')
# '2 and 4'
# 限制次数:
re.sub(r'a', 'x', 'aaa', count=1) # 'xaa'
# 大小写不敏感:
re.sub(r'hello', 'hi', 'HELLO', flags=re.I)

split 分割

re.split 按正则分割。多分隔符、保留分隔符、限制次数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import re
# 按逗号/分号/竖线分割:
re.split(r'[,;|]', 'a,b;c|d')
# ['a', 'b', 'c', 'd']
# 空白分割(含多个):
re.split(r'\s+', 'a b c')
# 捕获组保留分隔符:
re.split(r'(,)', 'a,b,c')
# ['a', ',', 'b', ',', 'c']
# 限制:
re.split(r',', 'a,b,c', maxsplit=1)
# ['a', 'b,c']
# 行分割:
re.split(r'\r?\n', text)
# 按段落:
re.split(r'\n\s*\n', text)
# str.split 不能多分隔符时用 re.split

分组

() 捕获分组、(?P<name>) 命名组、(?:) 非捕获、| 或。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import re
# 分组:
m = re.search(r'(\w+)@(\w+)\.(\w+)', '[email protected]')
m.group(0) # '[email protected]' 全匹配
m.group(1) # 'a'
m.group(2) # 'b'
m.groups() # ('a','b','com')
# 命名组:
m = re.search(r'(?P<user>\w+)@(?P<domain>\w+)', '[email protected]')
m.group('user') # 'a'
m.groupdict() # {'user': 'a', 'domain': 'b'}
# 非捕获:
re.search(r'(?:ab)+', 'abab')
# 或:
re.search(r'cat|dog', 'a dog')
# 分组引用替换:\1
# 嵌套分组编号从左括号数

标志

re.I 忽略大小写、re.M 多行、re.S 点匹配换行、re.X 详细模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import re
# re.I 忽略大小写:
re.search(r'hello', 'HELLO', re.I)
# re.M 多行锚点:
re.search(r'^line', 'a\nline b', re.M)
# re.S 点匹配换行:
re.search(r'a.b', 'a\nb', re.S)
# re.X 详细(注释空白):
pattern = re.compile(r'''
(\d{4}) # 年
[-/]
(\d{2}) # 月
''', re.X)
# 组合标志:
re.search(r'x', 'X', re.I | re.M)
# 编译可复用:
pat = re.compile(r'\d+', re.IGNORECASE)
pat.search('a1')
# 预编译提速 + 选项固化

常用模式

邮箱/手机号/URL/IP/中文等常用正则片段。校验业务格式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import re
# 邮箱:
EMAIL = r'^[^\s@]+@[^\s@]+\.[^\s@]+$'
# 中国大陆手机号:
MOBILE = r'^1[3-9]\d{9}$'
# URL:
URL = r'^https?://[^\s]+$'
# IPv4:
IP = r'^(\d{1,3}\.){3}\d{1,3}$'
# 中文:
CHINESE = re.compile(r'[\u4e00-\u9fff]+')
# 身份证(18 位):
ID = r'^\d{17}[0-9Xx]$'
# 日期:
DATE = r'^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$'
# 校验函数:
def is_email(s): return bool(re.match(EMAIL, s))
# 提取中文:
CHINESE.findall('hello 世界 你好')
# 颜色:
COLOR = r'^#[0-9a-fA-F]{6}$'

正则性能

预编译、避免灾难性回溯、限定宽度。大文本分块处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import re
# 预编译:
pattern = re.compile(r'\d+') # 模块级
pattern.findall(text)
# 避免灾难性回溯:
# BAD:嵌套量词
# re.match(r'(a+)+$', 'a' * 30 + '!') 极慢
# GOOD:简化
re.match(r'a+$', 'aaa')
# 限定宽度:
re.match(r'[a-z]{1,20}', 'x' * 100)
# 用字符类替代或:
# BAD:[a|b|c]
# GOOD:[abc]
# 大文本:
# 逐段处理而非全量
# 简单解析优先 str 方法:
# s.startswith / s.split
# 正则做结构匹配即可

19.构建与工程化

依赖管理、虚拟环境、测试、Lint 与 CI。

requirements.txt

固定依赖版本。pip freeze 导出、pip install -r 安装。锁定保证可复现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 导出当前环境:
pip freeze > requirements.txt
# 文件内容示例:
# requests==2.31.0
# httpx>=0.24.0
# # 注释说明用途
# 安装:
pip install -r requirements.txt
# 开发依赖分离:
# requirements-dev.txt 含测试工具
# 版本锁定策略:
# == 精确锁定
# >= 允许升级
# 生产环境锁定精确版本
# 现代替代:
# pyproject.toml 声明 + lock 文件
# uv 超快速安装

venv 实践

虚拟环境最佳实践:每项目一个、不提交 .venv、激活时机。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 创建:
python3 -m venv .venv
# 激活(POSIX):
source .venv/bin/activate
# 激活(Windows):
# .venv\Scripts\activate
# 检查当前解释器:
which python
# 退出:deactivate
# 删除重建(依赖损坏时):
# rm -rf .venv && python3 -m venv .venv
# .gitignore 加 .venv/
# 依赖安装进 venv:
# pip install 不要用系统环境
# 编辑器选择 venv 解释器

现代包管理

uv 极速依赖管理、poetry 依赖与打包。锁文件保证可复现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# uv(推荐,超快):
# pip install uv
uv venv # 建虚拟环境
uv add requests # 添加依赖
uv sync # 同步安装
# 替代 pip:
uv pip install -r requirements.txt
# poetry:
# poetry new mypkg
# poetry add requests
# poetry install
# pyproject.toml + poetry.lock
# 一致做法:
# 声明依赖 -> 锁文件 -> CI 用 lock 安装
# 锁文件保证所有人同一版本
# 升级:uv lock --upgrade

pytest 测试

test_ 前缀函数断言。fixture 共享、parametrize 参数化、断言报错清晰。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import pytest
from app import add
def test_add():
assert add(1, 2) == 3
def test_add_negative():
assert add(-1, 1) == 0
# 参数化:
@pytest.mark.parametrize('a,b,expected', [
(1, 2, 3),
(0, 0, 0),
])
def test_add_param(a, b, expected):
assert add(a, b) == expected
# fixture:
def setup_db():
db = create_db()
yield db
db.close()
def test_using_fixture(setup_db):
assert setup_db.query()
# 断言异常:
with pytest.raises(ValueError):
int('x')
# 运行:pytest 或 python3 -m pytest
# 覆盖率:pytest --cov

unittest 标准库

标准库测试框架。TestCase 类 + assert 方法。setUp/tearDown 生命周期。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import unittest
from app import add
class TestAdd(unittest.TestCase):
def setUp(self):
self.data = [1, 2]
def test_add(self):
self.assertEqual(add(1, 2), 3)
def test_types(self):
self.assertIsInstance(add(1, 2), int)
def test_raises(self):
with self.assertRaises(TypeError):
add('a', 1)
if __name__ == '__main__':
unittest.main()
# 运行:
# python3 -m unittest test_app
# 发现模式:
# python3 -m unittest discover
# 断言方法:
# assertEqual/assertTrue/assertIn
# assertAlmostEqual 浮点
# mock:unittest.mock 模拟依赖

Lint 与格式化

ruff 静态检查与格式化、black 风格。代码规范统一。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# ruff(推荐,超快):
# pip install ruff
ruff check . # 检查
ruff format . # 格式化
ruff check --fix . # 自动修复
# black:
# pip install black
black --line-length 88 .
# mypy 类型检查:
# pip install mypy
mypy src/
# 配置 pyproject.toml:
# [tool.ruff]
# line-length = 88
# [tool.mypy]
# strict = true
# 集成编辑器:
# 保存时格式化
# 提交前:pre-commit 钩子

打包与发布

pyproject 打包配置、构建 sdist/wheel、发布 PyPI。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# pyproject.toml:
# [project]
# name = "my-pkg"
# version = "0.1.0"
# description = "..."
# requires-python = ">=3.10"
# dependencies = ["requests"]
#
# [build-system]
# requires = ["setuptools>=68"]
# build-backend = "setuptools.build_meta"
#
# [tool.setuptools.packages.find]
# include = ["mypkg*"]
# 构建:
# pip install build
# python3 -m build
# 生成 dist/*.whl 与 tar.gz
# 发布:
# pip install twine
# twine upload dist/*
# 测试版:test.pypi.org
# 版本语义化:1.2.0
# 发布前测试安装:pip install .

CI 与部署

CI 阶段:lint、类型检查、测试、构建。GitHub Actions 配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# GitHub Actions:
# .github/workflows/ci.yml
# name: CI
# on: [push, pull_request]
# jobs:
# test:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v4
# - uses: actions/setup-python@v5
# with: python-version: '3.11'
# - run: pip install -e .[dev]
# - run: ruff check .
# - run: mypy src/
# - run: pytest --cov
# 缓存依赖:actions/cache
# 多版本矩阵:python-version: [3.10, 3.11, 3.12]
# 失败即阻止合并
# 部署:发布时上传 PyPI
# 环境变量:GitHub Secrets 存密钥

官方链接

直达官方文档与资源。

关于本速查

本页是 Python 3.11 的自包含速查手册,覆盖语言核心与标准库在真实项目中约 80% 的常见用法。内容偏向现代惯用法:f-string、列表推导式、生成器、上下文管理器、类型注解、数据类(dataclass),以及标准库中 collections、itertools、pathlib 等实用模块。Python 由 Guido van Rossum 于 1991 年发布,以简洁可读的语法与「内置电池」的标准库著称,是数据分析、机器学习、自动化脚本与 Web 开发(Django/FastAPI)的流行选择。 19 个章节各自聚焦一个主题:基础语法、变量、类型与引用语义、控制流、函数、字符串、列表与字典、内存与引用计数、类与面向对象、异常处理、输入输出、常见误区、并发(线程/异步)、网络、时间、进程、正则与构建工具(pip/venv)。每个小节都配有「概念介绍 + 可直接复制的代码片段」。 所有代码与文字均在浏览器本地渲染,无任何数据离开你的设备。权威参考见 Python 官方文档。

版本 2.1.0