变量赋值与多重赋值
x = 42 name = "Lei" a, b, c = 1, 2, 3 x, y = y, x # swap, no temp variable
Python 是动态类型,赋值就是把名字绑到对象上。一行多赋值很常见;交换两个变量用元组拆包,不用临时变量。
a = b = c = 0 # chained, all point to same object
first, *rest = [1, 2, 3, 4] # 1, [2,3,4]
Python 速查表,100+ 段地道 Python 代码片段,涵盖字符串/列表/字典/文件/异步,带真实例子。
x = 42 name = "Lei" a, b, c = 1, 2, 3 x, y = y, x # swap, no temp variable
Python 是动态类型,赋值就是把名字绑到对象上。一行多赋值很常见;交换两个变量用元组拆包,不用临时变量。
a = b = c = 0 # chained, all point to same object
first, *rest = [1, 2, 3, 4] # 1, [2,3,4]
n = 10 # int, 任意精度
pi = 3.14 # float, IEEE 754 双精度
z = 2 + 3j # complex
from decimal import Decimal
price = Decimal("0.1") + Decimal("0.2") # 精确 0.3int 任意精度(3 ** 200 也能算)。float 是 IEEE 754,0.1 + 0.2 不等于 0.3。算钱必用 Decimal,且参数要传字符串。
0.1 + 0.2 == 0.3 # False!
from fractions import Fraction; Fraction(1, 3)
bool(0) # False
bool("") # False
bool([]) # False
bool(None) # False
bool("False") # True (非空字符串都是 True)
bool([False]) # True (列表非空就是 True)假值只有这几个:0、0.0、""、[]、{}、set()、None、False。其他都是真值 —— 字符串 "False" 是真,列表 [False] 也是真。
if items: # 比 if len(items) > 0 更地道
x = a or "default" # a 为假值时取 default
x = None
if x is None: # ✅ 推荐
print("空值")
if x == None: # 能跑但不地道
passNone 是单例 —— 必须用 is None / is not None 判断,不要写 == None。is 比较对象身份,== 是值相等,可被重载坑掉你。
isinstance(x, int) # True if x is int (or bool!) isinstance(x, (int, float)) # 接受多种类型 type(x) is int # 严格相等,不认子类
用 isinstance 而不是 type() == —— isinstance 认继承。坑:bool 是 int 的子类,isinstance(True, int) 是 True。
# 排除 bool:isinstance(x, int) and not isinstance(x, bool)
print("a", "b", "c") # 默认空格分隔
print("a", "b", sep="-") # "a-b"
print("loading", end="") # 不换行
print("err msg", file=sys.stderr) # 打到 stderr
name = input("name: ") # 读一行(返回 str,不含换行)
age = int(input("age: ")) # 数字记得转print 接受 sep= 和 end=。input() 返回字符串(不含换行),要数字得手动 int() / float()。
range(5) # 0, 1, 2, 3, 4 range(2, 7) # 2, 3, 4, 5, 6 range(0, 10, 2) # 0, 2, 4, 6, 8 (步长) range(10, 0, -1) # 10, 9, 8, ..., 1 (反向) list(range(5)) # [0, 1, 2, 3, 4]
range(stop) / range(start, stop) / range(start, stop, step)。惰性的 —— 迭代时才出数。stop 不包含。
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+)是最快、最易读的格式化方式。:.2f 保留小数,:, 加千位分隔符,{var=} 自带变量名打印 —— 调试神器。
f"{x:>10}" # 右对齐宽 10f"{x:0>5}" # 左补 0 到 5 位f"{x:.0%}" # 百分比"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() 和 % 还能用,老项目里也常见。新代码推荐 f-string。
"a,b,c".split(",") # ['a', 'b', 'c']
"a b c".split() # ['a', 'b', 'c'] (默认按空白切,连续空白当一个)
",".join(["a", "b", "c"]) # "a,b,c"
"\n".join(lines) # 拼成多行字符串split() 不传参数按任意空白切并合并连续空白。join() 是字符串方法(分隔符在左),不是列表方法 —— 这是新手第一个坑。
"a,b,,c".split(",") # ["a", "b", "", "c"] (空段保留)"a,b,c".split(",", 1) # ["a", "b,c"] (限制 split 次数)" hello ".strip() # "hello"
" hello ".lstrip() # "hello "
" hello ".rstrip() # " hello"
"xxhelloxx".strip("x") # "hello"
"https://".rstrip("/") # "https:"strip() 默认去空白;传字符串是去那个字符集里的任意字符。坑:rstrip("ing") 是去 i/n/g 任意字符,不是去尾部的 "ing" 子串。
# Python 3.9+ 才有去前后缀 substring 的方法:
"running".removesuffix("ing") # "runn""running".removeprefix("ru") # "nning""hello world".replace("world", "Lei") # "hello Lei"
"a-b-c-d".replace("-", "_", 2) # "a_b_c-d" (限制次数)
import re
re.sub(r"\d+", "#", "abc 123 def 456") # "abc # def #" (正则替换)str.replace() 是普通子串替换,要正则用 re.sub()。replace() 返回新字符串 —— 字符串不可变。
url.startswith("https://")
filename.endswith((".jpg", ".png", ".gif")) # 元组:满足任一即可
"lei" in "hello lei" # 子串包含
"lei" in ["lei", "han"] # 列表 in O(n)
"lei" in {"lei", "han"} # 集合 in O(1)startswith / endswith 接受元组 —— 多扩展名一行搞定。in 在 list/string 上是 O(n),set/dict 上是 O(1),性能差距巨大。
"hello".upper() # "HELLO" "HELLO".lower() # "hello" "hello world".title() # "Hello World" "Hello".casefold() # "hello" (比 lower 更激进,能正确处理德语 ß) " Hello ".swapcase() # " hELLO "
lower() 处理 ASCII。casefold() 是大小写无关比较的正式做法 —— 能正确处理德语 ß → ss。
s = "hello" s[0] # 'h' s[-1] # 'o' s[1:4] # "ell" s[:3] # "hel" s[::-1] # "olleh" (反转,最骚的一行) s[::2] # "hlo" (步长 2)
切片是 [start:stop:step],stop 不包含。负数从末尾数。s[::-1] 是反转字符串的骚操作 —— 一行搞定。
"你好".encode("utf-8") # b'\xe4\xbd\xa0\xe5\xa5\xbd'
b'\xe4\xbd\xa0\xe5\xa5\xbd'.decode("utf-8") # "你好"
"abc".encode() # 默认 UTF-8
# 容错:errors="ignore" / "replace" / "strict"(默认)
b'\xff\xfe'.decode("utf-8", errors="replace") # "��"str → bytes 用 .encode(),bytes → str 用 .decode(),默认 UTF-8。errors="replace" / "ignore" 让坏字节不抛异常。
s = """第一行
第二行
第三行"""
import textwrap
textwrap.dedent("""
缩进的多行字符串
自动去掉前导空格
""")
textwrap.fill("超长一段话", width=40)三引号字符串保留换行。textwrap.dedent 去掉每行共同的前导空白 —— 内嵌 SQL/模板特别好用。
xs = [1, 2, 3] xs.append(4) # [1, 2, 3, 4] 单个元素 xs.extend([5, 6]) # [1, 2, 3, 4, 5, 6] 合并另一个可迭代 xs += [7, 8] # 等价于 extend xs.insert(0, 0) # [0, 1, 2, 3, ...] 指定位置插 xs.append([9, 10]) # [..., [9, 10]] 坑:嵌套了一层
append 加一个元素。extend(或 +=)合并一个可迭代。经典 bug:append([9, 10]) 是把整个列表当一个元素塞进去,套了一层。
xs = [1, 2, 3, 4, 5] xs[1:4] # [2, 3, 4] xs[:] # [1, 2, 3, 4, 5] 浅拷贝 xs[::-1] # [5, 4, 3, 2, 1] 反转 xs[1:3] = [20, 30] # 切片赋值:原地修改 xs[::2] = [10, 30, 50] # 步长切片赋值,两边长度要一致
切片语法和字符串一样。xs[:] 是浅拷贝的经典写法。切片赋值是原地修改 —— 批量替换很方便。
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 推导地道又快,一般比 for + append 还快。过滤 if 写在末尾;三元 if/else 必须写在 for 前面 —— 位置不一样,新手常搞混。
# 三个以上推导嵌套就拆 for 循环吧,可读性优先
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 和 filter 返回的是迭代器,要 list() 包一下才能看。一次性用 lambda 的场景 —— 写 list 推导更地道。
sorted([3, 1, 2]) # [1, 2, 3] 返回新列表 xs.sort() # 原地排序,返回 None sorted(words, key=len) # 按长度 sorted(users, key=lambda u: u.age) # 按属性 from operator import itemgetter, attrgetter sorted(rows, key=itemgetter(2)) # 比 lambda 快一点 sorted(xs, reverse=True) # 降序 # 多列排序:稳定排序,从次要到主要倒着 sort sorted(rows, key=lambda r: (r[0], -r[1])) # 第 1 列升、第 2 列降
sorted() 返回新列表,.sort() 原地排序且返回 None —— 不要写 xs = xs.sort(),那是个 None。key=itemgetter 比 lambda 快一点。Python sort 是稳定排序。
xs.reverse() # 原地反转,返回 None list(reversed(xs)) # 返回新列表 xs[::-1] # 也是反转,最简洁
.reverse() 原地修改;reversed() 返回迭代器(要 list() 包);xs[::-1] 返回新列表,最短。
# 不在乎顺序: 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() 去重丢顺序。dict.fromkeys() 去重且保留首次出现顺序(3.7 起 dict 有序,可放心用)。
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) 给你 (index, value) 对 —— 永远不要写 for i in range(len(seq))。start= 改起始索引。
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 把多个可迭代两两配对,默认按最短的截断。zip(*matrix) 转置矩阵。3.10+ strict=True 长度不一致直接抛错,避免静默截断。
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() 全为真才 True(空集合按空真规则返回 True,反直觉)。any() 至少一个为真就 True。传生成器表达式可短路提前结束。
sum([1, 2, 3]) # 6 sum([1, 2, 3], 100) # 106 (起始值) max([1, 2, 3]) min(["apple", "banana"], key=len) # "apple" max(users, key=lambda u: u.age) # 3.4+ 提供默认值: min([], default=0) # 不抛错
min/max 接受 key= 函数 —— 找最长字符串/最年轻用户特别方便。空可迭代会抛 ValueError,传 default= 不抛。
xs = [10, 20, 30, 40] xs.pop() # 删最后一个,返回 40,xs = [10, 20, 30] xs.pop(0) # 删第 0 个,返回 10,xs = [20, 30] xs.remove(30) # 按值删第一个匹配的 del xs[0] # 按索引删(不返回值) del xs[1:3] # 按切片删
pop 返回被删的元素。remove 按值删第一个匹配(找不到抛 ValueError)。del 是语句不是表达式,没返回值。
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())dict 是哈希表,平均 O(1) 查找。3.7 起 dict 保证插入顺序 —— 这是语言规范,不是实现细节,可放心依赖。
d = {"a": 1}
d["b"] # KeyError
d.get("b") # None
d.get("b", 0) # 0 (默认值)
# 嵌套取值的常见写法:
d.get("user", {}).get("name", "anonymous")d[k] 找不到抛 KeyError;d.get(k, 默认) 返回默认。嵌套取值时链式 .get("x", {}).get(...) 是常见安全写法。
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 有就返回,没有就设并返回默认值。defaultdict(list) 是更优雅的分组写法。
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 原地修改 a。{**a, **b} 或 3.9+ 的 a | b 返回新字典。冲突时后者覆盖前者。
{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()} # 共同键求和语法和 list 推导一样,但是 key:value。常用于过滤 None、反转 kv、临时建查表。
from collections import Counter
c = Counter("abracadabra")
# Counter({'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1})
c.most_common(3) # [('a', 5), ('b', 2), ('r', 2)]
c1 + c2 # 计数相加
c1 - c2 # 计数相减(负数被截)
c1 & c2, c1 | c2 # 取最小 / 最大Counter 是专门做计数的 dict 子类。most_common(n) 给前 n 个。支持 + - & | 做多重集合运算。
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)) # 嵌套 dictdefaultdict 首次访问就自动创建默认值。defaultdict(int) 计数,defaultdict(list) 分组,lambda 套娃做嵌套。
from collections import OrderedDict
od = OrderedDict()
od["a"] = 1
od["b"] = 2
od.move_to_end("a") # 移到末尾
od.popitem(last=False) # 弹首元素(普通 dict 没这俩方法)3.7 后普通 dict 已经保序,大部分场景 OrderedDict 可换成 dict。要用 move_to_end / popitem(last=False) 才有必要保留。
s = {1, 2, 3}
empty = set() # 注意:{} 是 dict 不是 set
s.add(4)
s.discard(99) # 不抛错;remove(99) 会抛 KeyError
2 in s # O(1)
len(s)set 无序、唯一、O(1) 成员判断。空集合是 set() 不是 {}({} 是空 dict)。discard() 是不抛错版的 remove()。
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 运算符最易读。也有 .union() .intersection() 等方法名版,可读性上运算符赢。
fs = frozenset([1, 2, 3])
# 可以做 dict 的键、放进 set 里
d = {fs: "value"}
s = {frozenset([1, 2]), frozenset([3, 4])}frozenset 不可变可哈希 —— 能当 dict 的 key、能放进 set 里。普通 set 都不行。
# 简单去重,不保留顺序: list(set([1, 2, 2, 3, 1])) # 顺序不稳定 # 保留顺序去重(3.7+ dict 保序): list(dict.fromkeys([1, 2, 2, 3, 1])) # [1, 2, 3]
set() 会丢顺序,dict.fromkeys() 保留首次出现顺序(3.7+ dict 保序)。
{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()})语法和 list / dict 推导一样,用 {} 但没 key:。算"去重总数"一行搞定。
t = (1, 2, 3) t = 1, 2, 3 # 括号可省,逗号才是关键 single = (1,) # 单元素元组要加逗号! empty = () # 不可变 —— 不能改: # t[0] = 99 → TypeError a, b, c = t # 拆包
元组靠逗号定义,不是括号。单元素元组要写 (1,) —— (1) 只是数字 1,最常见的坑。
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)
p.x, p.y # 3, 4
p[0] # 3 (也支持索引)
p._asdict() # {"x": 3, "y": 4}
Point(**{"x": 5, "y": 6})namedtuple 给元组加字段名 —— 不可变、轻、能 pickle。要默认值、加方法就改用 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: floatdataclass(3.7+)自动生成 __init__ / __repr__ / __eq__。可变默认值必须 field(default_factory=list),否则中招"可变默认参数"坑。frozen=True 出不可变版本。
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)解构带 *rest 能接中间或末尾。函数返回多值其实就是返回元组 —— 调用端再解构出来。
if x > 0:
print("正")
elif x < 0:
print("负")
else:
print("零")
# 三元(条件表达式):
label = "正" if x > 0 else "非正"标准分支。Python 写 elif 不是 else if。三元:值1 if 条件 else 值2 —— 条件在中间,跟 C/JS 不一样。
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, 8for-else:循环没被 break 才会跑 else —— 写"找不到就报告"的逻辑很简洁。
while queue:
x = queue.pop(0)
if x is None:
continue
if x == "stop":
break
process(x)
# while-else 同样:else 在没 break 时跑while 条件为真就一直跑。break 跳出整个循环,continue 跳过本次。while-else 也存在,逻辑同 for-else。
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})"3.10+ 才有的结构化模式匹配。| 表示或,case ... if x 是 guard。能匹配元组、字典、类。最后写 case _: 当 default。
# 在 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]海象 := 既赋值又返回。最适合"循环条件里要拿值又要判"和"推导式里要复用计算结果"两种场景。
def greet(name: str, greeting: str = "Hello") -> str:
return f"{greeting}, {name}!"
greet("Lei") # "Hello, Lei!"
greet("Lei", greeting="嗨") # 关键字参数
# 坑:可变默认值(永远不要这样写)
def add(item, items=[]): # ❌ 所有调用共享同一个 list
items.append(item)
return items
def add(item, items=None): # ✅ 正确写法
if items is None:
items = []
items.append(item)
return items默认参数在 def 时求值一次 —— 千万别用 [] 或 {} 这种可变默认值。要用 None 然后在函数里初始化。
add = lambda a, b: a + b sorted(rows, key=lambda r: r[1]) list(filter(lambda x: x > 0, nums)) # lambda 只能写单个表达式,不能有 return / for / 多行 # 多行逻辑就老老实实 def
lambda 只能单表达式,不能有语句、不能多行。短回调可以用,逻辑长就老老实实 def。
def fn(*args, **kwargs):
print(args) # 元组
print(kwargs) # 字典
fn(1, 2, 3, name="Lei")
# 转发参数(装饰器常用):
def wrapper(*args, **kwargs):
return inner(*args, **kwargs)
# 强制关键字参数(* 之后必须 keyword):
def connect(host, *, port=80, ssl=False):
pass
connect("x", port=443) # ✅
# connect("x", 443) # ❌ TypeError*args 把位置参数收成元组,**kwargs 把关键字参数收成字典。裸 * 强制后面必须 keyword 调用 —— API 设计利器,避免位置参数搞混。
def greet(name: str, times: int = 1) -> str:
return ("Hi " + name + "! ") * times
from typing import Optional, Union, Callable
def get(key: str) -> Optional[str]: ...
# 3.10+ 简化:
def get(key: str) -> str | None: ...
# 3.9+ 内置泛型:
def parse(items: list[int]) -> dict[str, int]: ...
# 函数类型:
def apply(fn: Callable[[int, int], int], a: int, b: int) -> int:
return fn(a, b)type hint 不强制,但 mypy / pyright 会检查。3.9+ 可以直接写 list[int]、dict[str, int];3.10+ 用 X | Y 替代 Union。
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)装饰器是接受函数、返回函数的函数。一定要用 @functools.wraps 保留原函数的 __name__ 和 docstring,否则反射全废。
# 经典 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]
循环里的 lambda 按引用捕获变量,不是按值 —— 最后全看到末值。修复:写 lambda i=i: i,用默认参数把当前值固定下来。
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函数内读外层变量 OK,但要赋值必须声明 global(模块级)或 nonlocal(外层函数)—— 不然就抛 UnboundLocalError。
from functools import partial
def power(base, exp):
return base ** exp
square = partial(power, exp=2)
cube = partial(power, exp=3)
square(5) # 25
cube(3) # 27
# 实战:给 sorted 传带固定参数的 key
sorted(rows, key=partial(getattr, default=None))partial 把一部分参数预先绑死,返回新可调用。比写 lambda 干净,专门用于参数固定的场景。
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__ 是创建实例后的初始化。self 是当前实例 —— 约定俗成,但每个方法定义都必须显式带上。
class Circle:
def __init__(self, r):
self.r = r
@property
def area(self):
return 3.14159 * self.r ** 2
@area.setter
def area(self, value):
self.r = (value / 3.14159) ** 0.5
c = Circle(5)
c.area # 不用 c.area(),直接像属性一样访问
c.area = 100 # 触发 setter@property 把方法变成属性访问。@prop.setter 定义写入逻辑。需要校验或派生时用,比 getter/setter 函数地道。
class User:
def __init__(self, name):
self.name = name
@classmethod
def from_dict(cls, d): # 工厂方法
return cls(d["name"])
@staticmethod
def is_valid_name(s): # 与类相关但不需要 cls / self
return s.isalpha()
u = User.from_dict({"name": "Lei"})
User.is_valid_name("Lei")classmethod 接收 cls(类本身),用于工厂方法。staticmethod 啥都不接收,是放在类命名空间里的工具函数。
class Money:
def __init__(self, amount, currency):
self.amount = amount
self.currency = currency
def __repr__(self):
return f"Money({self.amount}, '{self.currency}')"
def __str__(self):
return f"{self.amount:.2f} {self.currency}"
def __eq__(self, other):
return (self.amount, self.currency) == (other.amount, other.currency)
def __hash__(self):
return hash((self.amount, self.currency))
def __lt__(self, other):
return self.amount < other.amount
def __add__(self, other):
return Money(self.amount + other.amount, self.currency)__repr__ 给调试看,__str__ 给用户看;__eq__/__hash__ 让对象能进 set / dict 当 key;__lt__ 让 sorted() 能排;__add__ 让 + 工作。
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return "..."
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # 调父类 __init__
self.breed = breed
def speak(self): # 覆盖
return "Woof!"继承写 class Sub(Base)。super().method() 调父类。多继承按 MRO(方法解析顺序)。
from dataclasses import dataclass
@dataclass(frozen=True, slots=True) # 3.10+ slots=True
class Point:
x: float
y: float
p = Point(3, 4)
# p.x = 99 → FrozenInstanceError 不可变
# p.z = 99 → AttributeError slots 禁止新属性
hash(p) # frozen=True 自动可哈希frozen=True 让实例不可变且可哈希。slots=True(3.10+)用 __slots__ 省内存,且能防止打错字新建属性。
class Timer:
def __enter__(self):
import time
self.t = time.perf_counter()
return self
def __exit__(self, exc_type, exc, tb):
import time
print(f"耗时 {time.perf_counter() - self.t:.3f}s")
return False # False / None 表示不吞异常
with Timer():
slow_op()
# 或用 @contextmanager 装饰生成器:
from contextlib import contextmanager
@contextmanager
def open_db():
conn = connect()
try:
yield conn
finally:
conn.close()类实现 __enter__/__exit__,或更简单:写生成器 + @contextmanager。清理逻辑放 finally / __exit__ 才稳。
# 读文本(自动指定 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()永远用 with 打开 —— 出异常也会关。永远显式传 encoding="utf-8",别依赖系统默认(不同系统差异巨大,Windows 默认 cp936 害死人)。
from pathlib import Path
p = Path("data") / "users" / "lei.json" # / 操作符拼路径
p.exists()
p.is_file(), p.is_dir()
p.parent, p.name, p.stem, p.suffix
p.read_text(encoding="utf-8")
p.write_text("hello", encoding="utf-8")
list(Path(".").glob("*.py")) # 通配
list(Path(".").rglob("*.py")) # 递归
Path("out").mkdir(parents=True, exist_ok=True)pathlib(3.4+)用 Path 对象替代 os.path 字符串拼接。用 / 拼路径 —— 永远别字符串相加或 os.path.join。
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 操作文件,loads/dumps 操作字符串。有中文一定加 ensure_ascii=False,否则全变成 \u 转义。
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})CSV 文件打开一定加 newline="",否则 Windows 会插空行。DictReader / DictWriter 自动处理表头。
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 让 OS 管理临时路径。NamedTemporaryFile / TemporaryDirectory 出 with 自动清理,省心。
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 只有没出异常才跑。finally 不管有没有异常都跑(清理)。裸 except: 会吞掉 Ctrl+C —— 几乎永远是错的,要用 except Exception:。
raise ValueError("age 不能为负")
raise ValueError(f"非法值 {x}")
# 包装别人的异常(保留原始 traceback)
try:
int(s)
except ValueError as e:
raise ParseError("解析失败") from e
# 想隐藏原始链:
raise ParseError("...") from Noneraise X from Y 包装 Y 异常并保留链 —— traceback 两层都看得到。raise X from None 隐藏原始(很少需要)。
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")每个包定义一个基类异常,再派生子类 —— 调用方能选择抓"全家族"或具体类型。继承 Exception,不要继承 BaseException。
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(异常类型) 静默吞掉指定异常 —— 一行操作要忽略错误时比 try/except/pass 干净。
it = iter([1, 2, 3])
next(it) # 1
next(it) # 2
next(it) # 3
next(it) # StopIteration
next(it, "默认") # 第二个参数避免 StopIteration
# 任何实现 __iter__ + __next__ 的对象都是迭代器
class CountUp:
def __init__(self, n): self.n = n; self.i = 0
def __iter__(self): return self
def __next__(self):
if self.i >= self.n: raise StopIteration
self.i += 1
return self.iiter(obj) 拿迭代器,next(it) 推进。next(it, 默认) 安全版避免 StopIteration。for 循环背后就是在调这两个。
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 让函数变生成器 —— 惰性产值、状态保留。生成器表达式用 () 不是 [] —— 处理超大数据时省内存。
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 把迭代委托给另一个可迭代对象,含其所有子 yield。比手写 for + yield 简洁。
from itertools import chain, groupby, permutations, combinations, product, accumulate, islice
list(chain([1, 2], [3, 4])) # [1, 2, 3, 4]
list(chain.from_iterable([[1, 2], [3, 4]])) # [1, 2, 3, 4]
list(permutations([1, 2, 3], 2)) # 排列 (1,2),(1,3),(2,1)...
list(combinations([1, 2, 3], 2)) # 组合 (1,2),(1,3),(2,3)
list(product([0, 1], repeat=3)) # 笛卡尔积
list(accumulate([1, 2, 3, 4])) # [1, 3, 6, 10] 前缀和
list(islice(big_iter, 10)) # 取前 10 个
# groupby 必须先排序!
rows = sorted(rows, key=itemgetter("group"))
for g, items in groupby(rows, key=itemgetter("group")):
print(g, list(items))itertools 是标准库的迭代器工具箱。最大的坑:groupby 只合并连续相等的元素,用之前必须先按 key 排序。
from functools import reduce, lru_cache, cache
reduce(lambda a, b: a + b, [1, 2, 3, 4]) # 10
reduce(lambda a, b: a + b, [1, 2, 3, 4], 100) # 110 (初值)
@lru_cache(maxsize=None) # 自动记忆
def fib(n):
return n if n < 2 else fib(n-1) + fib(n-2)
@cache # 3.9+ 简化版 lru_cache(maxsize=None)
def f(x): ...reduce 把二元运算累折叠。@lru_cache 自动记忆,能把指数递归变线性。@cache(3.9+)是 lru_cache(maxsize=None) 的简写。
from itertools import zip_longest, takewhile, dropwhile
list(zip_longest([1, 2], ["a", "b", "c"], fillvalue="-"))
# [(1, 'a'), (2, 'b'), ('-', 'c')] 长度对不齐用 fillvalue 填
list(takewhile(lambda x: x < 5, [1, 3, 4, 6, 2])) # [1, 3, 4] 遇到 False 就停
list(dropwhile(lambda x: x < 5, [1, 3, 4, 6, 2])) # [6, 2] 跳到第一个 Falsezip_longest 用 fillvalue 补齐短的一边。takewhile / dropwhile 在第一个不满足条件处切开 —— 处理流式数据特别合适。
import asyncio
async def fetch(url):
await asyncio.sleep(1) # 模拟 IO
return f"data from {url}"
async def main():
data = await fetch("https://x.com")
print(data)
asyncio.run(main()) # 入口必须这一行async def 定义协程,await 让出控制权。asyncio.run() 是入口 —— 已经在事件循环里就不能再 run,会报错。
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 并发跑多个协程。return_exceptions=True 让一个失败不连累其他。3.11+ TaskGroup 是更安全的现代写法,异常处理更干净。
# 立刻调度,不等:
task = asyncio.create_task(fetch("x"))
# 之后再 await:
result = await task
# 限时:3.11+
async with asyncio.timeout(5):
await long_op()
# 旧写法 3.10-:
try:
await asyncio.wait_for(long_op(), timeout=5)
except asyncio.TimeoutError:
...
# 等任一个完成:
done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)create_task 立刻调度。asyncio.timeout(3.11+)用 async with 包裹做超时;老代码用 wait_for。
# 异步迭代器
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()async for / async with 用于实现了 __aiter__ / __aenter__ 的对象 —— HTTP 客户端、数据库驱动、流文件常见。
# 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 限并发数(比如 HTTP 同时只跑 10 个)。Queue 是生产消费模型,用 None 当哨兵告诉消费者结束。
# 3.8 写法(typing 模块): from typing import List, Dict, Tuple, Set def fn(xs: List[int], d: Dict[str, int]) -> Tuple[int, ...]: ... # 3.9+ 推荐用内置(不用 import): def fn(xs: list[int], d: dict[str, int]) -> tuple[int, ...]: ... # tuple 长度固定写法: Tuple[int, str, float] # 三元素,类型不同 tuple[int, ...] # 任意长度,元素都是 int
3.9+ 可直接用 list[int] / dict[str, int] / set[X],不用 from typing import 了。typing 的别名(List/Dict)还能用但不必。
from typing import Optional, Union def get(k: str) -> Optional[str]: ... # str 或 None def x(v: Union[int, str]): ... # int 或 str # 3.10+ 推荐用 | : def get(k: str) -> str | None: ... def x(v: int | str): ... # Optional[T] === T | None # 它不是"可省略",而是"可能为 None",别误解
Optional[T] 是"可能为 None",不是"可省略参数" —— 别误解。3.10+ 用 T | None / int | str 替代,更短。
from typing import TypedDict, NotRequired
class User(TypedDict):
name: str
age: int
email: NotRequired[str] # 3.11+
def greet(u: User) -> str:
return f"Hi, {u['name']}"
greet({"name": "Lei", "age": 30}) # ✅
# greet({"name": "Lei"}) # mypy 报错:缺 ageTypedDict 给字典形状加类型 —— JSON 风格数据特别适合,不用写整个类。3.11+ 的 NotRequired / Required 支持可选键。
from typing import Protocol
class HasName(Protocol):
name: str
def greet(self) -> str: ...
def shout(x: HasName) -> str:
return x.greet().upper()
# 任何有 name 属性 + greet() 方法的对象都满足 HasName
# 不用继承,结构匹配即可(structural typing)Protocol(3.8+)是带静态检查的鸭子类型 —— 形状对就算实现,不用继承。Python 版的 Go interface。
from typing import TypeVar, Generic
T = TypeVar("T")
def first(xs: list[T]) -> T:
return xs[0]
class Stack(Generic[T]):
def __init__(self): self._items: list[T] = []
def push(self, x: T): self._items.append(x)
def pop(self) -> T: return self._items.pop()
# 3.12+ 新语法(不用 TypeVar):
def first[T](xs: list[T]) -> T:
return xs[0]TypeVar 声明泛型参数。Generic[T] 让类支持泛型。3.12+ 新语法更简洁:def f[T](xs: list[T])。
from typing import Literal, Final, Annotated
def open_file(mode: Literal["r", "w", "rb", "wb"]): ...
open_file("r") # ✅
# open_file("x") # mypy 报错
MAX: Final = 100 # 不可重新赋值
PI: Final[float] = 3.14159
# Annotated 给类型加元数据(pydantic / fastapi 常用):
Age = Annotated[int, "0-150"]Literal 限定具体字面量 —— 模式参数特别好用。Final 标常量。Annotated 给类型加元数据,Pydantic / FastAPI 会读。
from typing import cast, overload
# 告诉类型检查器"我知道这里是什么"
x = cast(int, obj["count"]) # 运行时啥都不做,只骗 mypy
# 函数重载(多个签名,一份实现):
@overload
def fetch(x: int) -> User: ...
@overload
def fetch(x: str) -> list[User]: ...
def fetch(x):
# 真实实现
return _do(x)cast() 只骗 mypy,运行时啥都不做。@overload 声明多重签名(一份实现),适合返回类型随入参类型变的函数。
# ❌ 错误(所有调用共享同一个 list):
def add(item, items=[]):
items.append(item)
return items
add(1) # [1]
add(2) # [1, 2] 不是 [2]!
# ✅ 正确:
def add(item, items=None):
if items is None:
items = []
items.append(item)
return items默认参数在 def 时求值一次,[] / {} / set() 这种可变默认值会被所有调用共享 —— 调用间累积。永远用 None 哨兵 + 函数内初始化。
# ❌ 全是 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]
闭包按变量捕获不按值捕获 —— 所有 lambda 看到的都是末值。用 lambda i=i: 把当前值冻在默认参数里。
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 也变了!
切片 / .copy() / copy.copy() 都是浅拷贝,内层对象共享。copy.deepcopy() 递归克隆整棵树。要不要深拷贝看内部对象会不会被修改。
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 比身份(同一对象),== 比值。只对 None / True / False 这种单例用 is。小整数缓存(-5~256)是 CPython 实现细节,永远别依赖。
# ❌ 边遍历边删 —— 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]边遍历边删容器会漏元素或抛 RuntimeError。要么先 list() 拷一份再遍历,要么用推导式新建一个。
count = 0
def inc():
count += 1 # ❌ UnboundLocalError
# 因为函数里赋值了 count,Python 把它当本地变量
def inc():
global count # ✅ 显式声明
count += 1函数里任何地方给 count 赋值,Python 就认它是本地变量(哪怕在赋值前用也算)—— 不写 global / nonlocal 就 UnboundLocalError。
7 / 2 # 3.5 (Python 3 一律返回 float) 7 // 2 # 3 整除 -7 // 2 # -4 向下取整(不是向 0 取整!) divmod(7, 2) # (3, 1) 商和余数一起 # 来自 Python 2 的代码可能假设 / 是整除 —— 升 3 时小心
Python 3 中 / 一律返回 float,// 是向下取整(不是向 0 取整:-7 // 2 是 -4 不是 -3)。divmod() 同时给商和余数。
# ❌ 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)字符串不可变,循环里 s += x 是 O(n²)(每次拷贝整段)。先收集到 list 再 "".join() 是 O(n) —— 大数据量差距巨大。
# ❌ 吞掉 KeyboardInterrupt 和 SystemExit
try:
risky()
except:
pass
# ✅ 抓 Exception 就够了
try:
risky()
except Exception as e:
log.exception(e)裸 except 会抓 BaseException —— 连 Ctrl+C 和 sys.exit 都吞了。要用 except Exception: 抓正常错误就行。
可搜索的 Python 速查表,覆盖日常真在撸的 100+ 段地道 代码,不是凑数的 hello-world 入门列表。十五大分类: 基础(赋值与拆包、int/float/Decimal、bool 真假值、 None 单例、isinstance、range),字符串(f-string 含 格式说明符、split/join、strip 系列、replace 与 re.sub、 切片、UTF-8 编解码),列表(append vs extend 坑、list 推导含过滤和三元、sorted 带 key 稳定排序、enumerate、 zip 与 strict、保序去重),字典(.get 链式取值、 setdefault vs defaultdict、3.9 | 合并、Counter),集 合(| & - ^ 做并交差对称差、frozenset 可哈希、推导式 去重),元组(单元素逗号坑、namedtuple、dataclass 与 field(default_factory)、*rest 解构),控制流(if/ elif、for-else、while、海象 :=、3.10 match/case 含 guard 和解构),函数(默认参数坑、lambda 限制、*args/ **kwargs 与 keyword-only、type hint、@decorator 配 functools.wraps、partial),类(__init__、@property setter、@classmethod / @staticmethod、dunder 方法、 super 与 MRO、@dataclass frozen + slots、__enter__/ __exit__),文件(open 必加 encoding="utf-8"、pathlib 的 / 拼接、json 必加 ensure_ascii=False、csv 必加 newline=""、tempfile),异常(try/except/else/finally、 raise from、自定义异常继承、contextlib.suppress),迭 代(next 带默认、yield 生成器、yield from、itertools chain/groupby/permutations/accumulate、functools.reduce / @lru_cache、zip_longest / takewhile),异步(asyncio. run、gather 加 return_exceptions、create_task/timeout、 async for / async with、Queue + Semaphore、3.11 TaskGroup),类型提示(3.9 list[int]、3.10 X | None、 TypedDict + NotRequired、Protocol 鸭子接口、TypeVar / Generic、Literal / Final、cast / @overload),以及 9 个真烧时间的坑(可变默认参数、late binding 闭包、浅 拷贝 vs 深拷贝、is vs ==、遍历时修改容器、 UnboundLocalError、裸 except 吞 Ctrl+C、-7 // 2 是 -4、循环里字符串拼接 O(n²))。每条都带:双语标题、 可直接复制的真实代码、双语说明、1-2 条变式。搜索框 跨标题 / 代码 / 说明 / 变式四字段一起过滤,分类胶囊 缩范围,一键复制。完全在浏览器里跑,不连任何服务、 不上传。配合 SQL / curl / git / regex 速查覆盖整条 技术栈,搭 JSON Formatter 处理数据。
把内容粘贴或拖入工具面板。
点击按钮,在浏览器内本地处理,文件不上传。
一键复制结果或下载到本地。
适合穿插在写代码、查问题、做 Review、上线前的小任务里。
这些入口会把当前任务接到更完整的工具链里。
做你这行的人, 还会一起用这些。