本工具使用的開源套件

本工具程式碼中捆綁了 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