round() uses banker's rounding (round-half-to-even): round(2.5) == 2. pow(b, e, m) is fast modular exponentiation. divmod returns (quotient, remainder).
chr / ord / ASCII 与 Unicode
ord("A") # 65
chr(65) # 'A'
ord("中") # 20013
chr(0x1F600) # '😀'
"A" < "B" # True 按码点比较
[chr(c) for c in range(97, 123)] # a-z
ord() gives a character's Unicode code point; chr() goes back. String comparison is by code point. chr(0x1F600) builds emoji from a hex code point.
String (17)
f-string (the modern way to format)
name = "Lei"
age = 30
s = f"{name} is {age}" # "Lei is 30"
pi = 3.14159
s = f"{pi:.2f}" # "3.14"
n = 1234567
s = f"{n:,}" # "1,234,567"
s = f"{name=}, {age=}" # debug: "name='Lei', age=30"
f-string (3.6+) is the fastest and most readable. Use :.2f for decimals, :, for thousands separator, ={var}= for self-documenting debug prints.
Variants
f"{x:>10}" # 右对齐宽 10
f"{x:0>5}" # 左补 0 到 5 位
f"{x:.0%}" # 百分比
.format() and % — the legacy ways
"Hello, {}!".format("Lei")
"{0} {1} {0}".format("a", "b") # "a b a"
"{name} is {age}".format(name="Lei", age=30)
"Hello, %s! You are %d." % ("Lei", 30)
.format() and % still work and you will see them in older codebases. Prefer f-string for new code.
strip() defaults to whitespace; pass a string to strip ANY character in that set. Trap: rstrip("ing") strips i / n / g in any order, not the substring "ing".
str → bytes via .encode(), bytes → str via .decode(). Default codec is UTF-8. Use errors="replace" or "ignore" to handle malformed bytes without crashing.
find() returns the index or -1 when missing; index() raises ValueError instead. rfind searches from the right. count() tallies non-overlapping occurrences.
zfill pads with leading zeros (handles signs). ljust/rjust/center pad to a width with a fill char. f-string format specs (:03d, :^7) do the same inline.
splitlines handles \n, \r\n, \r uniformly (unlike split). partition splits on the FIRST occurrence into a 3-tuple — never raises, returns empties when absent.
is* predicates check the whole string. Trap: isdigit() accepts superscripts/fractions; use isdecimal() for strict 0-9, or isnumeric() for the widest set.
maketrans builds a translation table; translate applies it in one pass. The 3-arg form (from, to, delete) also deletes characters. Faster than chained replace().
正则 re:search / match / findall / finditer
import re
m = re.search(r"(\d+)-(\d+)", "id 12-34 end")
m.group(0) # "12-34"
m.group(1), m.group(2) # "12", "34"
re.match(r"\d+", "abc") # None (match 只从开头匹配)
re.findall(r"\d+", "a1b22c333") # ['1', '22', '333']
for m in re.finditer(r"\d+", text): # 拿到 Match 对象,含位置
print(m.group(), m.span())
# 预编译复用:
pat = re.compile(r"\w+@\w+\.\w+")
search scans anywhere; match anchors at the start. findall returns strings; finditer yields Match objects with .span(). Compile a pattern once if reused in a loop.
bytes.hex() / fromhex() convert to/from hex strings. base64.b64encode/decode work on bytes (not str). Use urlsafe_b64encode for tokens that go in URLs.
Same slice syntax as string. xs[:] is the classic shallow-copy idiom. Slice assignment mutates in place — convenient for batch replaces.
list 推导(List comprehension)
squares = [x * x for x in range(10)]
evens = [x for x in nums if x % 2 == 0]
matrix = [[i * j for j in range(5)] for i in range(5)]
# 双 for 拍平嵌套:
flat = [x for row in matrix for x in row]
# 带 else 的写法(注意 if 位置变了):
labels = ["even" if x % 2 == 0 else "odd" for x in nums]
List comprehensions are Pythonic and usually faster than equivalent for-loops with .append(). Filter goes at the end; ternary goes BEFORE the for clause.
Variants
# 三个以上推导嵌套就拆 for 循环吧,可读性优先
map / filter — 函数式写法
list(map(str, [1, 2, 3])) # ["1", "2", "3"]
list(map(lambda x: x * 2, nums)) # 不推荐,写成推导更地道
list(filter(lambda x: x > 0, nums)) # 不推荐,写成 [x for x in nums if x > 0]
# 多个可迭代一起:
list(map(lambda a, b: a + b, [1, 2], [10, 20])) # [11, 22]
map and filter return iterators — wrap in list() to materialize. For one-shot transforms with lambda, list comprehension is more Pythonic.
sorted() returns a new list; .sort() mutates in place and returns None. Use key= for custom criteria; itemgetter is faster than lambda. Sort is stable.
.reverse() mutates; reversed() returns an iterator (wrap in list()); xs[::-1] returns a new list and is the shortest.
列表去重(保留顺序)
# 不在乎顺序:
list(set(xs))
# 保留首次出现顺序(3.7+ dict 有序):
list(dict.fromkeys(xs))
# 复杂对象去重(按某 key):
seen = set()
uniq = [x for x in xs if not (x in seen or seen.add(x))]
set() de-dupes but loses order. dict.fromkeys() de-dupes AND preserves first-seen order (works since 3.7 where dicts are ordered).
enumerate — 同时拿索引和值
for i, v in enumerate(items):
print(i, v)
for i, v in enumerate(items, start=1): # 从 1 开始
print(i, v)
# 反例(不要写):
for i in range(len(items)):
print(i, items[i]) # 不地道
enumerate(seq) yields (index, value) pairs — never write for i in range(len(seq)). start= sets the first index (default 0).
zip — 多个可迭代并行遍历
names = ["Lei", "Han", "Mei"]
ages = [30, 25, 28]
for n, a in zip(names, ages):
print(n, a)
# 转字典:
d = dict(zip(names, ages))
# 转置矩阵:
list(zip(*matrix))
# 3.10+ strict 模式,长度不一致就抛错:
zip(names, ages, strict=True)
zip pairs up multiple iterables — stops at the shortest by default. zip(*matrix) transposes. Python 3.10+ strict=True errors on length mismatch.
all / any — 列表逻辑判断
all([True, True, True]) # True
all([]) # True (空集合默认 True,反直觉)
any([False, False, True]) # True
any([]) # False
# 实战:
all(x > 0 for x in nums) # 全部为正
any(s.startswith("error") for s in logs)
all() returns True if every element is truthy (vacuous truth: empty → True). any() returns True if at least one is truthy. Pass a generator for short-circuit.
pop returns the removed element. remove deletes the FIRST match by value (raises if not found). del is a statement, not an expression.
index / count — 查找与计数
xs = [10, 20, 30, 20]
xs.index(20) # 1 第一个匹配的下标
xs.index(20, 2) # 3 从下标 2 开始找
xs.count(20) # 2
99 in xs # False
# 找不到 index 会抛 ValueError,先判 in 或 try:
if 20 in xs:
i = xs.index(20)
list.index(x) returns the first matching position (raises ValueError if absent); the optional start arg resumes the search. count(x) tallies occurrences.
deque gives O(1) append/pop on BOTH ends — use it for queues and BFS instead of list (whose pop(0) is O(n)). maxlen makes a self-trimming sliding window.
嵌套列表展平
nested = [[1, 2], [3, 4], [5]]
# 一层嵌套:
flat = [x for row in nested for x in row] # [1,2,3,4,5]
# 或 itertools:
from itertools import chain
flat = list(chain.from_iterable(nested))
# 任意深度递归展平:
def flatten(xs):
for x in xs:
if isinstance(x, list):
yield from flatten(x)
else:
yield x
For one level, a double-for comprehension or chain.from_iterable flattens. For arbitrary depth, write a recursive generator with yield from.
列表分块(chunk)
def chunks(xs, n):
for i in range(0, len(xs), n):
yield xs[i:i + n]
list(chunks([1, 2, 3, 4, 5], 2)) # [[1,2],[3,4],[5]]
# 配对(3.12+ 用 itertools.batched):
from itertools import batched # Python 3.12+
list(batched("ABCDEFG", 3)) # [('A','B','C'),('D','E','F'),('G',)]
Slice every n with range(0, len, n) to chunk a list. Python 3.12+ ships itertools.batched(iterable, n) which works on any iterable, not just sliceable lists.
Dict (12)
dict 基本操作
d = {"name": "Lei", "age": 30}
d["name"] # "Lei"
d["email"] = "x@y.z" # 加键
del d["age"] # 删键
"name" in d # 判键存在
len(d)
list(d.keys()), list(d.values()), list(d.items())
Dicts are hash maps with O(1) average lookup. Since 3.7, dicts preserve insertion order — relied on by language spec, not an implementation detail.
d[k] raises KeyError; d.get(k, default) returns the default. Chain .get with empty dict default for safe nested access.
dict.setdefault — 一次性"取 or 初始化"
groups = {}
for name, group in pairs:
groups.setdefault(group, []).append(name)
# 等价于:
from collections import defaultdict
groups = defaultdict(list)
for name, group in pairs:
groups[group].append(name)
setdefault returns the existing value, or sets and returns the default. defaultdict(list) is the cleaner alternative for grouping.
dict.update — 合并字典
a = {"x": 1, "y": 2}
b = {"y": 20, "z": 30}
a.update(b) # a 变成 {"x":1, "y":20, "z":30}
# 3.5+ 解包合并(返回新字典):
merged = {**a, **b}
# 3.9+ | 运算符:
merged = a | b
a |= b # 原地合并
update mutates a in place. {**a, **b} or (3.9+) a | b returns a NEW dict. Later keys win on conflicts.
dict 推导式
{x: x * x for x in range(5)} # {0:0, 1:1, 2:4, 3:9, 4:16}
{k: v for k, v in items if v is not None} # 过滤 None 值
{v: k for k, v in d.items()} # 反转 kv
# 多个源:
{k: a[k] + b[k] for k in a.keys() & b.keys()} # 共同键求和
Same syntax as list comprehension but with key:value pairs. Useful for filtering Nones, inverting dicts, building lookup tables on the fly.
Counter is a dict subclass for counting hashables. most_common(n) gives the top n. Supports + - & | operators for multiset math.
collections.defaultdict
from collections import defaultdict
counts = defaultdict(int) # 默认 0
for w in words:
counts[w] += 1 # 不用先判 in
buckets = defaultdict(list)
for x in items:
buckets[x.group].append(x)
nested = defaultdict(lambda: defaultdict(int)) # 嵌套 dict
defaultdict creates the default value on first access. defaultdict(int) for counting, defaultdict(list) for grouping, lambda for nested defaults.
pop(k) removes and returns; pop(k, default) avoids KeyError. popitem() removes the LAST inserted pair (LIFO since 3.7). clear() empties the dict in place.
dict.keys()/items() are live VIEW objects that reflect later changes, and keys() supports set operators (& | -) directly — handy for diffing two dicts.
ChainMap searches multiple dicts in order without copying — first hit wins. Perfect for layered config (CLI args > env > file > defaults) without merging.
Sort a dict by value via sorted(d.items(), key=lambda kv: kv[1]). max(d, key=d.get) gives the key with the highest value. heapq.nlargest for top-N pairs.
Set (7)
set 创建与基本操作
s = {1, 2, 3}
empty = set() # 注意:{} 是 dict 不是 set
s.add(4)
s.discard(99) # 不抛错;remove(99) 会抛 KeyError
2 in s # O(1)
len(s)
Sets are unordered, unique, O(1) membership. EMPTY set is set(), not {} (that's an empty dict). discard() is the safe remove.
set 交并差
a = {1, 2, 3}
b = {2, 3, 4}
a | b # 并集 {1, 2, 3, 4}
a & b # 交集 {2, 3}
a - b # 差集 {1}
a ^ b # 对称差 {1, 4}
a <= b # 子集判断
a >= b # 超集判断
Set operators are the readable way. Also available as .union() .intersection() .difference() .symmetric_difference() — but operators win on conciseness.
frozenset — 可哈希的不可变集合
fs = frozenset([1, 2, 3])
# 可以做 dict 的键、放进 set 里
d = {fs: "value"}
s = {frozenset([1, 2]), frozenset([3, 4])}
frozenset is immutable and hashable — can be a dict key or live inside another set. Regular sets cannot do either.
set() loses order. dict.fromkeys() preserves first-seen order (since 3.7 dicts are insertion-ordered).
集合推导式
{x * x for x in range(10)} # {0, 1, 4, 9, ..., 81}
{w.lower() for w in words} # 去重 + 小写
# 一句话拿独立词数:
unique_words = len({w.lower() for w in text.split()})
Same syntax as list/dict comprehensions but with {} (no key:). One-liner for "count distinct" style problems.
The *_update methods (or |= &= -=) mutate a set in place. isdisjoint() checks for zero overlap without building the intersection. pop() removes an arbitrary element.
namedtuple gives tuples named fields — immutable, lightweight, picklable. For more features (defaults, methods), use dataclass instead.
dataclass — 现代数据类
from dataclasses import dataclass, field
@dataclass
class User:
name: str
age: int = 0
tags: list[str] = field(default_factory=list) # 可变默认值必须 factory
u = User("Lei", 30)
u.age = 31 # 默认可变
@dataclass(frozen=True) # 不可变版本
class Point:
x: float
y: float
dataclass (3.7+) auto-generates __init__, __repr__, __eq__. Use field(default_factory=list) for mutable defaults. frozen=True for immutable.
tuple 解构与 *rest
a, b, c = (1, 2, 3)
first, *rest = [1, 2, 3, 4] # 1, [2, 3, 4]
*init, last = [1, 2, 3, 4] # [1, 2, 3], 4
a, *_, b = range(10) # 头尾,中间扔掉
# 函数返回多值常用:
def divmod_(a, b):
return a // b, a % b
q, r = divmod_(10, 3)
Tuple unpacking with *rest captures the middle / tail. Returning multiple values from a function is just returning a tuple — unpack at the call site.
NamedTuple 类语法 + 默认值
from typing import NamedTuple
class Point(NamedTuple):
x: int
y: int
label: str = "origin" # 带默认值
p = Point(3, 4)
p.x, p.label # 3, "origin"
p._replace(x=10) # 返回新元组(不可变)
# 比 collections.namedtuple 多了类型注解,更现代
typing.NamedTuple lets you write namedtuples as a class with type hints and defaults. ._replace() returns a modified copy (tuples are immutable). Cleaner than the functional form.
dataclass:__post_init__ / asdict / replace
from dataclasses import dataclass, asdict, replace, field
@dataclass
class Rect:
w: float
h: float
area: float = field(init=False) # 不进 __init__
def __post_init__(self):
self.area = self.w * self.h # 派生字段
r = Rect(3, 4)
r.area # 12
asdict(r) # {'w':3,'h':4,'area':12}
replace(r, w=5) # 返回改了 w 的新实例
__post_init__ runs after generated __init__ — use it for derived/validated fields. field(init=False) keeps a field out of the constructor. asdict()/replace() are dataclass helpers.
enum.Enum — 枚举
from enum import Enum, auto
class Color(Enum):
RED = 1
GREEN = 2
BLUE = auto() # 自动 3
Color.RED # <Color.RED: 1>
Color.RED.name # 'RED'
Color.RED.value # 1
Color(1) # <Color.RED: 1> 按值反查
list(Color) # 可迭代
# 3.11+ StrEnum:成员就是字符串
from enum import StrEnum
class Env(StrEnum):
DEV = "dev"
Enum gives named constants with .name/.value, value lookup via Color(1), and iteration. auto() assigns sequential values. 3.11+ adds StrEnum/IntEnum for string/int members.
Control flow (8)
if / elif / else
if x > 0:
print("正")
elif x < 0:
print("负")
else:
print("零")
# 三元(条件表达式):
label = "正" if x > 0 else "非正"
Standard branching. Python uses elif (not else if). Ternary syntax: value_if_true if cond else value_if_false (cond in the MIDDLE — different from C/JS).
for / for-else
for x in items:
if x < 0:
break
else:
print("没遇到负数") # break 没触发才会跑
for i in range(5): print(i)
for i in range(2, 10, 2): print(i) # 2, 4, 6, 8
for-else: the else runs when the loop completes without break — perfect for "search and notify if not found" patterns.
while / break / continue
while queue:
x = queue.pop(0)
if x is None:
continue
if x == "stop":
break
process(x)
# while-else 同样:else 在没 break 时跑
while runs as long as the condition is truthy. break exits the loop; continue skips to the next iteration. while-else also exists.
match / case(3.10+ 模式匹配)
def http_status(code: int) -> str:
match code:
case 200 | 201 | 204:
return "OK"
case 301 | 302:
return "Redirect"
case 400 | 404:
return "Client error"
case n if 500 <= n < 600: # guard
return "Server error"
case _:
return "Unknown"
# 解构匹配:
match point:
case (0, 0): return "原点"
case (x, 0): return f"X 轴 {x}"
case (0, y): return f"Y 轴 {y}"
case (x, y): return f"({x},{y})"
Structural pattern matching (3.10+). Use | for or, guards with `if`, and tuple/dict/class patterns for destructuring. Always end with case _: as the default.
walrus := 海象运算符(3.8+)
# 在 while 条件里赋值 + 判断:
while chunk := f.read(4096):
process(chunk)
# if 里赋值 + 用:
if (n := len(items)) > 10:
print(f"too many: {n}")
# 推导里复用计算结果:
[y for x in data if (y := expensive(x)) is not None]
The walrus := assigns AND returns. Best for loop conditions that need the value AND a check, or list comps that re-use a computed value.
match 进阶:类 / 字典 / 通配解构
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
match obj:
case Point(x=0, y=0):
print("原点")
case Point(x=0, y=y):
print(f"Y 轴 {y}")
case {"type": "user", "name": name}: # 字典模式
print(f"用户 {name}")
case [first, *rest]: # 序列模式
print(first, rest)
case _:
print("其他")
match supports class patterns (Point(x=0, y=y) binds y), dict patterns (matches a subset of keys), and sequence patterns with *rest. 3.10+.
链式比较与 in 范围判断
x = 5
0 < x < 10 # True 链式比较,等价 0 < x and x < 10
1 <= month <= 12
"a" <= ch <= "z" # 判小写字母
# in 判区间/集合:
if status in (200, 201, 204):
...
if grade in "ABCDF":
...
Python allows chained comparison: 0 < x < 10 means 0 < x and x < 10, evaluating x once. Use `in (a, b, c)` for "equals one of" instead of multiple ==.
try 里的 else 与提前 return
# 卫语句(guard clause)替代深层嵌套:
def process(user):
if user is None:
return None
if not user.active:
return None
# 主逻辑不用层层缩进
return do_work(user)
# 替代 if/else 金字塔,可读性大涨
Guard clauses (early return on invalid input) flatten nested if/else pyramids — handle edge cases up front, keep the happy path at the base indentation level.
*args collects positional args as a tuple; **kwargs collects keyword args as a dict. Bare * forces everything after to be keyword-only — great API hygiene.
Type hints are optional but checked by mypy / pyright. 3.9+ accepts list[int] and dict[str, int] directly; 3.10+ accepts X | Y instead of Union.
装饰器 decorator
from functools import wraps
import time
def timed(fn):
@wraps(fn) # 保留 fn 的 __name__ / docstring
def inner(*args, **kwargs):
t = time.perf_counter()
result = fn(*args, **kwargs)
print(f"{fn.__name__} took {time.perf_counter()-t:.3f}s")
return result
return inner
@timed
def slow():
time.sleep(1)
A decorator is a function that takes and returns a function. Always use @functools.wraps to preserve the original's __name__ and docstring.
闭包与 late binding 坑
# 经典 late binding 坑:
fns = [lambda: i for i in range(3)]
[f() for f in fns] # [2, 2, 2],不是 [0, 1, 2]
# 修复:默认参数当时绑定
fns = [lambda i=i: i for i in range(3)]
[f() for f in fns] # [0, 1, 2]
Lambdas in a loop capture the variable by REFERENCE, not by value — all see the final i. Fix with `i=i` default arg to snapshot at def time.
global 与 nonlocal
count = 0
def inc():
global count # 不写就报 UnboundLocalError
count += 1
def make_counter():
n = 0
def inc():
nonlocal n # 修改外层函数的 n(不是 global)
n += 1
return n
return inc
Inside a function, reading a name from outer scope works, but ASSIGNING to it requires global (module level) or nonlocal (enclosing function).
A bare / (3.8+) marks the preceding params as positional-only — callers can't use their names. Combined with *, you fully control the call convention of your API.
带参数的装饰器(三层)
from functools import wraps
def retry(times=3):
def deco(fn):
@wraps(fn)
def inner(*args, **kwargs):
for i in range(times):
try:
return fn(*args, **kwargs)
except Exception:
if i == times - 1:
raise
return inner
return deco
@retry(times=5)
def flaky(): ...
A parameterized decorator is a function returning a decorator returning a wrapper — three nested levels. @retry(times=5) calls the outer factory first, then decorates.
singledispatch turns a function into a generic that picks an implementation by the FIRST argument's type — Python's built-in single-dispatch polymorphism.
Prefer built-ins: sum() for addition, math.prod() (3.8+) for multiplication. Reach for reduce() only for non-trivial folds, pairing it with the operator module.
递归与尾递归(Python 无 TCO)
import sys
sys.getrecursionlimit() # 默认 1000
sys.setrecursionlimit(10000) # 谨慎调大,可能栈溢出崩溃
# Python 不做尾递归优化,深递归改迭代:
def factorial(n): # 递归版,n 大会 RecursionError
return 1 if n <= 1 else n * factorial(n - 1)
def factorial_iter(n): # 迭代版,安全
r = 1
for i in range(2, n + 1):
r *= i
return r
Python has NO tail-call optimization — deep recursion hits RecursionError (~1000). Convert hot recursive code to iteration; only raise the limit as a last resort.
Class (13)
class 基础 / __init__ / self
class User:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
def greet(self) -> str:
return f"Hi, I'm {self.name}"
u = User("Lei", 30)
u.greet()
__init__ runs after instance creation. self is the instance — by convention, but you MUST pass it explicitly in every method definition.
Implement __enter__/__exit__ on a class — or simpler: write a generator and decorate with @contextmanager. Always cleanup in finally / __exit__.
类属性 vs 实例属性
class Dog:
species = "Canis" # 类属性,所有实例共享
def __init__(self, name):
self.name = name # 实例属性,各自独立
a, b = Dog("A"), Dog("B")
Dog.species # "Canis"
a.species = "Wolf" # 在 a 上新建实例属性,遮蔽类属性
b.species # 仍 "Canis"
# 坑:类属性用可变对象会被所有实例共享!
class Bad:
items = [] # ❌ 所有实例共用一个 list
Class attributes are shared across all instances; assigning self.x creates a per-instance shadow. Trap: a mutable class attribute (items = []) is shared by every instance.
__slots__ — 省内存禁动态属性
class Point:
__slots__ = ("x", "y") # 不再有 __dict__
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(3, 4)
# p.z = 5 → AttributeError 禁止新属性
# 大量实例时省内存(不为每个对象建 __dict__),
# 还能拦住打错字凭空建属性的 bug
__slots__ replaces the per-instance __dict__ with a fixed layout — saves memory at scale and blocks accidental new attributes (typo protection). Loses dynamic attrs.
abc.ABC — 抽象基类
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self) -> float: ...
class Circle(Shape):
def __init__(self, r): self.r = r
def area(self): return 3.14159 * self.r ** 2
# Shape() → TypeError 不能实例化抽象类
# 忘实现 area 的子类也不能实例化
c = Circle(5)
Subclass ABC and mark methods with @abstractmethod to force subclasses to implement them — instantiating the base (or an incomplete subclass) raises TypeError.
Define __call__ to make instances callable like functions — useful for stateful "functions" (configured transformers, counters). callable(obj) checks for it.
__getitem__ / __len__ / __iter__ — 容器协议
class Deck:
def __init__(self, cards):
self._cards = cards
def __len__(self):
return len(self._cards)
def __getitem__(self, i):
return self._cards[i] # 支持 deck[0]、切片、for、in
deck = Deck(["A", "K", "Q"])
len(deck) # 3
deck[0] # "A"
for c in deck: ... # __getitem__ 即可迭代
"A" in deck # True 自动支持
Implementing __len__ + __getitem__ makes a class behave like a sequence — indexing, slicing, iteration, and `in` all work for free, no explicit __iter__ needed.
Define just __eq__ and one of __lt__/__le__/__gt__/__ge__, then @total_ordering fills in the rest — less boilerplate for fully ordered types.
File & I/O (10)
open 与 with — 读写文件
# 读文本(自动指定 encoding,别依赖系统默认!)
with open("a.txt", "r", encoding="utf-8") as f:
content = f.read()
# 一行行读(大文件友好,不全部加载)
with open("big.log", encoding="utf-8") as f:
for line in f:
process(line.rstrip("\n"))
# 写文本
with open("out.txt", "w", encoding="utf-8") as f:
f.write("hello\n")
f.writelines(["a\n", "b\n"])
# 追加
with open("out.txt", "a", encoding="utf-8") as f:
f.write("more\n")
# 二进制
with open("img.png", "rb") as f:
data = f.read()
Always use `with` — it closes the file even on exception. ALWAYS pass encoding="utf-8" explicitly; the OS default differs across platforms.
pathlib (3.4+) replaces os.path string-mangling with real Path objects. Use the / operator to join — never string concat or os.path.join.
json — 读写 JSON
import json
# 字符串 ↔ 对象
data = json.loads('{"a": 1}')
s = json.dumps({"a": 1}) # 紧凑
s = json.dumps(obj, indent=2, ensure_ascii=False) # 缩进 + 中文不转义
# 文件 ↔ 对象
with open("data.json", encoding="utf-8") as f:
data = json.load(f)
with open("out.json", "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
load/dump = file; loads/dumps = string. ALWAYS pass ensure_ascii=False if you have non-ASCII data, otherwise 中文 becomes \u escapes.
csv — 读写 CSV
import csv
# 读
with open("data.csv", encoding="utf-8", newline="") as f:
reader = csv.DictReader(f)
for row in reader:
print(row["name"], row["age"])
# 写
with open("out.csv", "w", encoding="utf-8", newline="") as f:
writer = csv.DictWriter(f, fieldnames=["name", "age"])
writer.writeheader()
writer.writerow({"name": "Lei", "age": 30})
ALWAYS open CSV files with newline="" — otherwise Windows writes blank lines between rows. DictReader/DictWriter handle headers automatically.
tempfile — 临时文件 / 目录
import tempfile
# 临时文件(自动删除):
with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f:
f.write("data")
path = f.name
# 临时目录:
with tempfile.TemporaryDirectory() as d:
p = Path(d) / "x.txt"
p.write_text("hi")
# 出 with 时 d 整个被删
tempfile creates OS-managed temp paths that survive across reboots. NamedTemporaryFile + TemporaryDirectory auto-cleanup on context exit.
glob matches paths by wildcard: * (any), ? (one char), [..] (set), ** with recursive=True (any depth). pathlib's .glob/.rglob are the modern equivalent.
pickle — Python 对象序列化
import pickle
data = {"users": [1, 2, 3], "obj": SomeClass()}
# 存:
with open("data.pkl", "wb") as f:
pickle.dump(data, f)
# 读:
with open("data.pkl", "rb") as f:
data = pickle.load(f)
# 字节串:
b = pickle.dumps(data)
# ⚠️ 永远不要 pickle.load 不可信来源,可执行任意代码
pickle serializes almost any Python object to bytes (use "wb"/"rb"). SECURITY: never unpickle untrusted data — it can execute arbitrary code. Use JSON for interchange.
configparser reads/writes INI files. Values come back as strings; use getint/getfloat/getboolean for typed access. Sections behave like nested dicts.
gzip / zipfile — 压缩文件
import gzip, zipfile
# gzip 单文件:
with gzip.open("data.json.gz", "wt", encoding="utf-8") as f:
f.write(json_str)
with gzip.open("data.json.gz", "rt", encoding="utf-8") as f:
content = f.read()
# zip 多文件:
with zipfile.ZipFile("out.zip", "w", zipfile.ZIP_DEFLATED) as z:
z.write("a.txt")
z.writestr("b.txt", "内容") # 直接写字符串
with zipfile.ZipFile("out.zip") as z:
z.extractall("dest/")
names = z.namelist()
gzip.open compresses a single stream ("rt"/"wt" for text). zipfile bundles multiple files — ZIP_DEFLATED to compress, writestr to add content directly, extractall to unpack.
Exception (8)
try / except / else / finally
try:
n = int(s)
data = fetch(n)
except ValueError as e:
print(f"输入不是数字: {e}")
except (KeyError, IndexError) as e: # 多种异常一起
print(f"数据问题: {e}")
except Exception as e: # 兜底
print(f"其他错误: {e}")
raise # 抛出去
else:
print("没出错才跑这里")
finally:
cleanup() # 出不出错都跑
else runs only when NO exception fired. finally always runs (cleanup). Bare `except:` catches even KeyboardInterrupt — almost always wrong; use `except Exception:`.
raise — 抛异常 / 链式异常
raise ValueError("age 不能为负")
raise ValueError(f"非法值 {x}")
# 包装别人的异常(保留原始 traceback)
try:
int(s)
except ValueError as e:
raise ParseError("解析失败") from e
# 想隐藏原始链:
raise ParseError("...") from None
raise X from Y wraps Y while preserving the cause chain — the traceback shows both. raise X from None hides the original (rarely what you want).
自定义异常
class APIError(Exception):
"""所有 API 错误的基类"""
class RateLimitError(APIError):
def __init__(self, retry_after: int):
super().__init__(f"rate limited, retry after {retry_after}s")
self.retry_after = retry_after
try:
call_api()
except RateLimitError as e:
time.sleep(e.retry_after)
except APIError:
log.exception("api failed")
Define a base exception per package and subclass it. Lets callers catch the broad family OR the specific type. Always inherit from Exception, not BaseException.
contextlib.suppress — 优雅忽略异常
from contextlib import suppress
# 老写法:
try:
os.remove("not-exist.txt")
except FileNotFoundError:
pass
# 新写法:
with suppress(FileNotFoundError):
os.remove("not-exist.txt")
# 多个异常:
with suppress(KeyError, IndexError):
value = data["k"][0]
suppress(ExceptionType) is a context manager that swallows those exceptions silently — cleaner than try/except/pass for one-line ops.
assert — 断言
def divide(a, b):
assert b != 0, "除数不能为 0"
return a / b
assert isinstance(x, int)
assert all(s > 0 for s in scores), f"有非正分数: {scores}"
# ⚠️ python -O 会去掉所有 assert,不要用 assert 做线上输入校验!
# 它是给开发期的"不变量检查"
assert cond, msg raises AssertionError when cond is false. WARNING: running with python -O strips all asserts — never use them for production input validation, only dev-time invariant checks.
finally always runs — even after return or during an exception — so it's the place to release resources. Trap: a return inside finally overrides the try's return and swallows exceptions.
log.exception(msg) inside an except block logs the message PLUS the full traceback automatically. traceback.format_exc() returns the traceback as a string when you need it.
ExceptionGroup / except* (3.11+)
# 3.11+ 一次抛多个异常(并发任务常见):
raise ExceptionGroup("多个失败", [
ValueError("bad value"),
TypeError("bad type"),
])
# 用 except* 分别处理:
try:
async with asyncio.TaskGroup() as tg:
...
except* ValueError as eg:
print("值错误组:", eg.exceptions)
except* TypeError as eg:
print("类型错误组:", eg.exceptions)
ExceptionGroup (3.11+) bundles multiple exceptions raised together (e.g. from a TaskGroup). except* matches and handles each type within the group separately.
iter(obj) returns an iterator. next(it) advances it. next(it, default) is the safe way to avoid StopIteration. Most for loops just call these for you.
generator / yield — 惰性生成
def count_up(n):
i = 0
while i < n:
yield i # 每次 yield 暂停,下次 next() 继续
i += 1
for x in count_up(5): print(x)
# 生成器表达式(节省内存):
squares = (x * x for x in range(1_000_000))
sum(squares)
yield turns a function into a generator — produces values lazily, one at a time, holding state between calls. Generator expressions use () instead of [].
yield from — 委托给子生成器
def flatten(nested):
for item in nested:
if isinstance(item, list):
yield from flatten(item) # 递归
else:
yield item
list(flatten([1, [2, [3, [4]]], 5])) # [1, 2, 3, 4, 5]
yield from delegates iteration to another iterable, including all sub-yields. Cleaner than a manual for-loop with yield.
zip_longest pads the shorter iterable. takewhile / dropwhile slice an iterable at the first False predicate — useful for streaming data.
itertools.count / cycle / repeat — 无限迭代器
from itertools import count, cycle, repeat, islice
# 无限计数:
for i in count(10, 2): # 10, 12, 14, ...
if i > 20: break
# 循环:
colors = cycle(["red", "green", "blue"])
[next(colors) for _ in range(5)] # red,green,blue,red,green
# 重复:
list(repeat("x", 3)) # ['x','x','x']
# 配 islice 截断无限流:
list(islice(count(), 5)) # [0,1,2,3,4]
count/cycle/repeat are INFINITE — always bound them with islice, takewhile, or break, never list() them directly. cycle repeats a sequence forever.
itertools.starmap / pairwise / tee
from itertools import starmap, pairwise, tee
# starmap:参数已经成组时用,省去 lambda 解包
list(starmap(pow, [(2,3),(3,2)])) # [8, 9]
# pairwise(3.10+):相邻配对
list(pairwise([1,2,3,4])) # [(1,2),(2,3),(3,4)]
# 算相邻差:
[b - a for a, b in pairwise(nums)]
# tee:把一个迭代器复制成 n 个独立迭代器
it1, it2 = tee(iter(data), 2)
starmap(f, tuples) unpacks each tuple as args. pairwise (3.10+) yields adjacent pairs — great for diffs. tee splits one iterator into n independent ones.
generator.send / close — 协程式生成器
def averager():
total, count = 0, 0
avg = None
while True:
x = yield avg # yield 既出值又收 send 进来的值
total += x
count += 1
avg = total / count
g = averager()
next(g) # 预激活,跑到第一个 yield
g.send(10) # 10.0
g.send(20) # 15.0
g.close() # 关闭生成器
A generator can RECEIVE values via .send() at the yield point (yield is two-way). Prime it with next() first. .close() raises GeneratorExit to shut it down.
enumerate + zip 组合迭代
names = ["Lei", "Han"]
ages = [30, 25]
# 同时要索引和多列:
for i, (name, age) in enumerate(zip(names, ages), start=1):
print(i, name, age)
# 反转字典遍历:
for v, k in sorted((v, k) for k, v in d.items()):
print(k, v)
Nest zip inside enumerate to get index + multiple parallel values at once — remember to parenthesize the unpacked zip tuple: enumerate(zip(a, b)).
async def declares a coroutine. await yields control. asyncio.run() is the canonical entrypoint — never call run() inside an already-running loop.
asyncio.gather — 并发执行
async def main():
urls = ["a", "b", "c"]
# 串行(慢):
results = [await fetch(u) for u in urls]
# 并发(快 N 倍):
results = await asyncio.gather(*[fetch(u) for u in urls])
# 一个失败别全部炸:
results = await asyncio.gather(*tasks, return_exceptions=True)
# 3.11+ TaskGroup(推荐写法,更安全):
async with asyncio.TaskGroup() as tg:
ts = [tg.create_task(fetch(u)) for u in urls]
results = [t.result() for t in ts]
gather runs coroutines concurrently. return_exceptions=True prevents one failure from cancelling the rest. 3.11+ TaskGroup is the modern, safer pattern.
create_task schedules immediately. asyncio.timeout (3.11+) is the new context-manager way to limit time; older code uses wait_for.
async for / async with
# 异步迭代器
async def fetch_pages():
async for chunk in client.stream("..."):
yield chunk
# 异步上下文管理
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
text = await resp.text()
Use async for / async with on objects that define __aiter__ / __aenter__ — common in HTTP clients, DB drivers, file streams.
asyncio.Queue / Semaphore — 并发原语
# Semaphore 限并发:
sem = asyncio.Semaphore(10)
async def fetch_limited(url):
async with sem:
return await fetch(url)
# Queue 生产消费:
q = asyncio.Queue(maxsize=100)
async def producer():
for x in items:
await q.put(x)
await q.put(None) # 哨兵
async def consumer():
while (x := await q.get()) is not None:
process(x)
q.task_done()
Semaphore caps concurrent operations (e.g., HTTP calls). Queue is the producer/consumer primitive — use None / sentinel to signal completion.
asyncio.to_thread (3.9+) offloads a blocking sync function to a thread so it won't freeze the event loop — perfect for legacy sync IO. For CPU work use a ProcessPoolExecutor.
async 生成器与 contextmanager
from contextlib import asynccontextmanager
@asynccontextmanager
async def get_conn():
conn = await connect()
try:
yield conn
finally:
await conn.close()
async def main():
async with get_conn() as conn:
await conn.query("...")
# 异步生成器:
async def gen():
for i in range(3):
await asyncio.sleep(0.1)
yield i
@asynccontextmanager turns an async generator into an `async with` resource. An async def with yield is an async generator — consume it with `async for`.
asyncio.run() is the single top-level entrypoint — it creates and closes the loop. Calling it inside a running coroutine raises RuntimeError; just await instead.
Protocol (3.8+) is duck typing with static checking — any object with the right shape satisfies it, no inheritance needed. Python's answer to Go interfaces.
cast() is a typing-only assertion (zero runtime cost). @overload declares multiple signatures for the same function — useful when return type depends on input type.
Callable / ParamSpec — 函数类型
from typing import Callable
# 接收两个 int 返回 int 的函数:
def apply(fn: Callable[[int, int], int], a, b) -> int:
return fn(a, b)
# 任意参数:
handler: Callable[..., None]
# 3.10+ ParamSpec 保留被装饰函数的签名:
from typing import ParamSpec, TypeVar
P = ParamSpec("P"); R = TypeVar("R")
def deco(fn: Callable[P, R]) -> Callable[P, R]:
def inner(*args: P.args, **kwargs: P.kwargs) -> R:
return fn(*args, **kwargs)
return inner
Callable[[ArgTypes], Ret] types a function; Callable[..., R] accepts any args. ParamSpec (3.10+) lets a decorator preserve the wrapped function's exact signature for type checkers.
TYPE_CHECKING — 避免循环导入
from __future__ import annotations # 注解延迟求值
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from .models import User # 只在类型检查时导入
def greet(u: User) -> str: # 字符串注解,运行时不真导入
return u.name
TYPE_CHECKING is False at runtime, True for type checkers — put import-only-for-types inside it to break circular imports. Pair with `from __future__ import annotations`.
Self (3.11+) types fluent/builder methods that return their own instance — subclass-aware, cleaner than a TypeVar. assert_type is a no-op that lets type checkers verify an inferred type.
NewType creates a distinct type that's an int at runtime but separate to type checkers — prevents mixing up UserId and OrderId even though both are ints. Zero runtime cost.
Default args are evaluated ONCE at def time. A default [] / {} / set() is shared across ALL calls — accumulates between calls. Always use None sentinel + initialize inside.
late binding 闭包(循环里的 lambda)
# ❌ 全是 2:
fns = [lambda: i for i in range(3)]
[f() for f in fns] # [2, 2, 2]
# ✅ 默认参数当时固定:
fns = [lambda i=i: i for i in range(3)]
[f() for f in fns] # [0, 1, 2]
Closures capture VARIABLES not VALUES — all lambdas see the final i. Fix by binding via default arg `i=i` so the current value is snapshotted at def time.
浅拷贝 vs 深拷贝
import copy
a = [[1, 2], [3, 4]]
b = a # 同一个对象(引用)
b = a[:] # 浅拷贝:外层新,内层共享
b = a.copy() # 浅拷贝
b = copy.copy(a) # 浅拷贝
b = copy.deepcopy(a) # 深拷贝(递归)
a[0].append(99)
print(b) # 浅拷贝下 b 也变了!
list slice / .copy() / copy.copy() are SHALLOW — inner objects are shared. copy.deepcopy() recursively clones everything. Choose based on whether inner data is mutated.
is vs ==(身份 vs 值相等)
a = [1, 2, 3]
b = [1, 2, 3]
a == b # True 值相等
a is b # False 不是同一个对象
# is 只对单例可靠:None / True / False / 小整数缓存等
x is None # ✅ 推荐
x == None # 能跑,不地道
# 坑:小整数缓存(-5 到 256)
a = 256; b = 256
a is b # True (缓存)
a = 257; b = 257
a is b # 一般是 False
is checks identity (same object), == checks value. Use `is None` / `is True` / `is False` only — those are singletons. Never rely on small-int caching.
遍历时修改容器
# ❌ 边遍历边删,RuntimeError 或漏删
for x in list_:
if x.bad:
list_.remove(x)
# ✅ 倒序删 / 或新建一个:
for i in range(len(list_) - 1, -1, -1):
if list_[i].bad:
del list_[i]
xs = [x for x in xs if not x.bad] # 最地道
# dict 也同样:
for k in list(d.keys()): # list() 拷一份再删
if d[k] is None:
del d[k]
Mutating a list/dict while iterating it skips items or raises RuntimeError. Either iterate a copy (`list(d.keys())`) or build a new container via comprehension.
If you ASSIGN to a name anywhere in a function, Python treats it as local for the WHOLE function — even before the assignment line. Use global/nonlocal to declare otherwise.
In Python 3, / always returns float. // is floor division (rounds toward NEGATIVE infinity — -7 // 2 == -4, not -3). divmod() returns (quotient, remainder).
字符串拼接的性能陷阱
# ❌ O(n²) — 字符串不可变,每次 + 都拷贝整段
s = ""
for x in items:
s += str(x)
# ✅ O(n) — 收集再 join
parts = []
for x in items:
parts.append(str(x))
s = "".join(parts)
# 或者列表推导 + join:
s = "".join(str(x) for x in items)
Strings are immutable — `s += x` in a loop is O(n²) because every iteration copies the whole string. Collect into a list and join at the end for O(n).
Bare `except:` catches BaseException — including KeyboardInterrupt and SystemExit. You can't Ctrl+C out. Use `except Exception:` to catch all normal errors.
float is IEEE 754 binary — 0.1 + 0.2 != 0.3. Never == compare floats; use math.isclose(a, b). For money/exact decimals, use Decimal with STRING inputs.
[[0]*3]*3 makes the OUTER list repeat the SAME inner list reference 3 times — mutating one row changes all. Build rows independently with a comprehension instead.
and / or 返回值不是布尔
1 and 2 # 2 and 返回最后一个真值(或第一个假值)
0 and 2 # 0
"" or "默认" # "默认" or 返回第一个真值(或最后一个值)
None or [] # []
# 实战:取默认值
name = user_input or "anonymous"
# 坑:当 0 / "" / [] 是合法值时会被当假值替换掉!
count = config.get("count") or 10 # count=0 也会变 10,应该用 get 默认值
and/or return one of the OPERANDS, not a bool: `a or b` is a if truthy else b. Great for defaults — but trap: a valid 0/""/[] gets replaced. Use dict.get(k, default) instead.
默认 dict.get 不创建键,[] 才创建
d = {}
d.get("k") # None,不会创建键
d["k"] # KeyError,且不创建
# defaultdict 的坑:访问即创建!
from collections import defaultdict
dd = defaultdict(list)
_ = dd["x"] # 只是读,但 "x" 已被建出来了!
"x" in dd # True 意外地存在了
# 想读又不创建用 .get:
dd.get("y") # None,不创建
defaultdict creates the key on ANY access via [], even a read — so `_ = dd["x"]` silently adds "x". Use .get() when you want to read without creating.
Python 3 refuses to compare unrelated types (int vs str, anything vs None) — sorting a mixed list raises TypeError. Normalize with a key= that maps everything to one comparable type.
is 比较字符串/数字的陷阱
a = "hello"
b = "hello"
a is b # 可能 True(小字符串驻留),但别依赖
x = "hello world!"
y = "hello world!"
x is y # 可能 False
# 坑:== 才是值比较,is 是身份比较
# 只对 None / True / False 用 is:
if val is None: ...
# 永远别用 is 比字符串或数字内容
String interning and small-int caching are CPython implementation details — `a is b` for equal strings/ints is unreliable. Use `is` ONLY for None/True/False; use == for value equality.
Only hashable (immutable) objects can be dict keys or set members — lists/dicts/sets raise "unhashable type". Use a tuple instead; for custom classes, implement __hash__/__eq__ over immutable fields.
A module's top-level code runs ONCE per process at first import, then is cached — module-level mutable state (caches, lists) persists across all callers. Avoid heavy IO at module top level; it fires on import.
What this tool does
Searchable Python cheat sheet, 100+ idiomatic snippets
developers actually type — not hello-world filler.
Fifteen categories: basics (assign, numeric + Decimal,
bool, None, isinstance, range), string (f-string +
format spec, split / join, strip, replace + re.sub,
slicing, UTF-8), list (append vs extend trap,
comprehension with filter + ternary, sorted + key,
enumerate, zip strict, dedupe preserving order),
dict (.get, setdefault vs defaultdict, 3.9 | merge,
Counter), set (| & - ^ operators, frozenset, dedupe),
tuple (single-element comma, namedtuple, dataclass
with field(default_factory), *rest), control (if /
for-else / while, walrus :=, 3.10 match / case with
guards), function (mutable-default trap, lambda,
*args / **kwargs / keyword-only, type hints,
decorator + functools.wraps, partial), class
(__init__, @property, classmethod / staticmethod,
dunder, super + MRO, dataclass frozen + slots,
__enter__ / __exit__), file (encoding="utf-8" always,
pathlib, json ensure_ascii=False, csv newline="",
tempfile), exception (try / except / else / finally,
raise from, custom hierarchy, contextlib.suppress),
iter (next + default, yield, yield from, itertools
chain / groupby / permutations / accumulate,
functools.reduce + @lru_cache, zip_longest /
takewhile), async (asyncio.run, gather +
return_exceptions, create_task / timeout, async for
/ with, Queue + Semaphore, 3.11 TaskGroup), typing
(3.9 list[int], 3.10 X | None, TypedDict +
NotRequired, Protocol, TypeVar / Generic, Literal /
Final, cast / overload), and pitfalls (mutable
default, late-binding closure, shallow vs deep copy,
is vs ==, mutate while iterate, UnboundLocalError,
bare except, -7 // 2 == -4, O(n²) string concat).
Every entry: bilingual title, runnable code,
bilingual description, 1-2 variants. Search title /
code / description / variants together. 100% client-
side. Pair with SQL / curl / git / regex cheat
sheets and JSON Formatter.
Tool details
Input
Text
The page exposes text boxes, numeric controls, file pickers, or structured inputs depending on the tool.
Output
Live result + Copy
The result area focuses on usable output, with copy, download, or preview actions when supported.
Privacy
May use a live lookup
A network call is detected in the component, so redact sensitive data when appropriate.
Save / share
No account required
Open the page and use it; whether results survive refresh depends on the tool.
Performance budget
Initial JS <= 30 KB
No WASM budget is declared, keeping the tool quick to open on mobile.
Best fit
Developer & DevOps · Developer
Category and role tags drive related tools, internal links, and quick fit checks.
How to use
1
1. Input
Paste or drop your content into the tool panel.
2
2. Process
Click the button. All processing is local in your browser.
3
3. Copy / Download
Copy the result or download to disk in one click.
How Python Cheatsheet fits into your work
Use it in the small gaps between coding, reviewing, debugging, and shipping.
Developer jobs
Formatting, validating, shrinking, or inspecting code-adjacent text.
Preparing snippets for documentation, tickets, commits, or handoff.
Checking a small payload quickly without switching tools.
Developer checks
Run irreversible transforms like minify or obfuscate on a copy.
Keep secrets out of pasted snippets unless the tool explicitly stays local.
Use your normal tests or linter before shipping transformed code.
Good next steps
These links move the current task into a more complete workflow.
You are parsing JSON into a dict and forget whether setdefault or defaultdict fits. Type setdefault in the search box and the dict entries surface with runnable code, the description, and a variant, so you copy the right one without leaving your editor to read full docs.
Check an async snippet before shipping concurrent requests
Before firing 1000 HTTP calls, filter to async and read the asyncio.gather and Semaphore entries. Each shows the exact call and a 3.11 TaskGroup variant, so you ship the version your interpreter actually supports.
Common pitfalls
Searching for a class name like Counter but missing it because you typed counter; search is case-aware on code, try lowercase prose terms like count too.
Copying a 3.10 match-case snippet onto a 3.8 interpreter; check the version tag and use the pre-3.10 fallback shown alongside.
Assuming the search only matches titles; it scans title, code, description and variants, so a term inside a code variant still surfaces the entry.
Privacy
Everything runs in your browser. The cheat sheet is one static page and search filters an in-memory array of snippets; no Python runs, nothing uploads, zero network requests. Safe behind corporate proxies and on air-gapped machines.
FAQ
Related tools
Hand-picked utilities that pair well with this one.