Skip to main content

Python Cheatsheet — 100+ Idiomatic Snippets with Pitfalls, Async, and Type Hints

Python cheat sheet — 100+ idiomatic Python snippets for string, list, dict, file, async, with real examples.

  • Runs locally
  • Category Developer & DevOps
  • Best for Formatting, validating, shrinking, or inspecting code-adjacent text.
170 snippets
Basics (11)

Variable assignment & multiple assignment

x = 42
name = "Lei"
a, b, c = 1, 2, 3
x, y = y, x   # swap, no temp variable

Python is dynamically typed — assignment binds a name to an object. Multi-assign on one line; tuple unpack to swap without a temp.

Variants
a = b = c = 0   # chained, all point to same object
first, *rest = [1, 2, 3, 4]   # 1, [2,3,4]

Numeric types: int, float, complex, decimal

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.3

int has no limit (3 ** 200 just works). float is IEEE 754 — 0.1 + 0.2 != 0.3. For money, always use Decimal with string inputs.

Variants
0.1 + 0.2 == 0.3   # False!
from fractions import Fraction; Fraction(1, 3)

bool and truthy / falsy values

bool(0)         # False
bool("")        # False
bool([])        # False
bool(None)      # False
bool("False")   # True (非空字符串都是 True)
bool([False])   # True (列表非空就是 True)

Falsy: 0, 0.0, "", [], {}, set(), None, False. Everything else is truthy — including the string "False" and the list [False].

Variants
if items:  # 比 if len(items) > 0 更地道
x = a or "default"  # a 为假值时取 default

None — the singleton

x = None
if x is None:        # ✅ 推荐
    print("空值")
if x == None:        # 能跑但不地道
    pass

None is a singleton — use `is None` / `is not None`, never `== None`. `is` checks identity, `==` checks equality (and can be overridden).

Type checking with isinstance

isinstance(x, int)             # True if x is int (or bool!)
isinstance(x, (int, float))    # 接受多种类型
type(x) is int                 # 严格相等,不认子类

Use isinstance, not type() == — isinstance respects inheritance. Trap: bool is a subclass of int, so isinstance(True, int) returns True.

Variants
# 排除 bool:isinstance(x, int) and not isinstance(x, bool)

print 与 input

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 accepts sep= and end=. input() returns a string (no newline) — convert with int() / float() if you need a number.

range — 区间序列

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). It's LAZY — doesn't materialize until you iterate. Stop is exclusive.

位运算 & | ^ ~ << >>

5 & 3      # 1    按位与
5 | 3      # 7    按位或
5 ^ 3      # 6    按位异或
~5         # -6   按位取反 (-(x+1))
1 << 4     # 16   左移 (= 1 * 2**4)
256 >> 2   # 64   右移
(0b1010).bit_count()   # 2   (3.10+ 数二进制里有几个 1)

Bitwise ops work on ints. ~x equals -(x+1). x << n is x * 2**n. Python 3.10+ adds int.bit_count() (popcount) and int.bit_length().

Variants
(255).bit_length()   # 8
int("ff", 16)        # 255  按进制解析
bin(10), hex(255), oct(8)

数字进制与格式化

bin(10)      # '0b1010'
hex(255)     # '0xff'
oct(8)       # '0o10'
int("1010", 2)    # 10   按二进制解析
int("ff", 16)     # 255
f"{255:#x}"  # '0xff'   带前缀
f"{10:08b}"  # '00001010'   补零到 8 位
1_000_000    # 下划线分隔,提高可读性

bin/hex/oct return prefixed strings; int(s, base) parses back. Use f-string mini-language (#x, 08b) for formatted output. Underscores in literals are ignored.

round / abs / pow / divmod

round(3.14159, 2)    # 3.14
round(2.5)           # 2    (银行家舍入:四舍六入五取偶)
round(3.5)           # 4
abs(-7)              # 7
pow(2, 10)           # 1024
pow(2, 10, 1000)     # 24   (2**10 % 1000,模幂,密码学常用)
divmod(17, 5)        # (3, 2)   商和余数

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.

split / join — string ↔ list

"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() with no args splits on any whitespace AND collapses runs. join() is a STRING method (delimiter on the left), not a list method.

Variants
"a,b,,c".split(",")   # ["a", "b", "", "c"]  (空段保留)
"a,b,c".split(",", 1)   # ["a", "b,c"]  (限制 split 次数)

strip / lstrip / rstrip — 去空白

"  hello  ".strip()           # "hello"
"  hello  ".lstrip()          # "hello  "
"  hello  ".rstrip()          # "  hello"
"xxhelloxx".strip("x")        # "hello"
"https://example.com/docs/".rstrip("/")  # "https://example.com/docs"

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".

Variants
# Python 3.9+ 才有去前后缀 substring 的方法:
"running".removesuffix("ing")   # "runn"
"running".removeprefix("ru")    # "nning"

replace — 字符串替换

"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() does plain substring substitution. For regex, use re.sub(). replace() returns a NEW string — strings are immutable.

startswith / endswith / in

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 accept a TUPLE — perfect for multi-extension checks. The `in` operator is O(n) on list/string, O(1) on set/dict.

大小写转换 upper / lower / title / casefold

"hello".upper()        # "HELLO"
"HELLO".lower()        # "hello"
"hello world".title()  # "Hello World"
"Hello".casefold()     # "hello"  (比 lower 更激进,能正确处理德语 ß)
"  Hello  ".swapcase() # "  hELLO  "

lower() handles ASCII. casefold() is the case-insensitive comparison standard — handles German ß → ss correctly.

字符串切片与索引

s = "hello"
s[0]        # 'h'
s[-1]       # 'o'
s[1:4]      # "ell"
s[:3]       # "hel"
s[::-1]     # "olleh"  (反转,最骚的一行)
s[::2]      # "hlo"    (步长 2)

Slicing is [start:stop:step], stop exclusive. Negative indexes count from the end. s[::-1] is the canonical reverse one-liner.

编码与解码 encode / decode

"你好".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 via .encode(), bytes → str via .decode(). Default codec is UTF-8. Use errors="replace" or "ignore" to handle malformed bytes without crashing.

多行字符串与 textwrap

s = """第一行
第二行
第三行"""
import textwrap
textwrap.dedent("""
    缩进的多行字符串
    自动去掉前导空格
""")
textwrap.fill("超长一段话", width=40)

Triple-quoted strings preserve newlines. textwrap.dedent strips the common leading whitespace — handy for embedded SQL or templates.

find / index / count — 子串定位

s = "hello world hello"
s.find("hello")     # 0    找不到返回 -1
s.find("xyz")       # -1
s.rfind("hello")    # 12   从右找
s.index("world")    # 6    找不到抛 ValueError
s.count("hello")    # 2    出现次数
s.count("l")        # 5

find() returns the index or -1 when missing; index() raises ValueError instead. rfind searches from the right. count() tallies non-overlapping occurrences.

对齐与填充 ljust / rjust / center / zfill

"5".zfill(3)         # "005"   左补零
"7".rjust(5)         # "    7"  右对齐
"7".rjust(5, "0")    # "00007"
"hi".ljust(5, ".")   # "hi..."
"hi".center(7, "*")  # "**hi***"
# f-string 等价写法:
f"{5:03d}"           # "005"
f"{'hi':^7}"         # "  hi   "

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 / partition — 行与分段

"a\nb\r\nc".splitlines()      # ['a', 'b', 'c']  跨平台换行都认
"a\nb\n".split("\n")          # ['a', 'b', '']  末尾多个空段
"key=value".partition("=")    # ('key', '=', 'value')
"no-sep".partition("=")       # ('no-sep', '', '')   找不到也不抛错
"a=b=c".partition("=")        # ('a', '=', 'b=c')   只切第一个

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.

str 判定方法 isdigit / isalpha / isspace

"123".isdigit()      # True
"12.3".isdigit()     # False  (点不是数字)
"abc".isalpha()      # True
"abc123".isalnum()   # True
"  ".isspace()       # True
"Hello".istitle()    # True
"ABC".isupper()      # True
"²".isdigit()        # True!   上标也算 (用 isdecimal 更严)

is* predicates check the whole string. Trap: isdigit() accepts superscripts/fractions; use isdecimal() for strict 0-9, or isnumeric() for the widest set.

str.translate / maketrans — 批量字符映射

table = str.maketrans("aeiou", "12345")
"hello".translate(table)        # "h2ll4"
# 删除字符:
str.maketrans("", "", "aeiou")
"hello world".translate(str.maketrans("", "", "aeiou"))  # "hll wrld"
# 全角转半角等批量替换比多次 replace 快

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.

Variants
re.split(r"\s+", text)   # 按正则切
re.sub(r"\d", "#", s)    # 正则替换

bytes 与 hex / base64

b = b"hello"
b.hex()                      # '68656c6c6f'
bytes.fromhex("68656c6c6f")  # b'hello'
import base64
base64.b64encode(b"hi")      # b'aGk='
base64.b64decode(b"aGk=")    # b'hi'
# URL 安全变体:
base64.urlsafe_b64encode(data)

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.

List (18)

append / extend / insert

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 adds ONE element. extend (or +=) merges an iterable. The classic bug: append([9, 10]) appends the list as a single nested element.

列表切片与切片赋值

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]   # 步长切片赋值,两边长度要一致

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 / sort / key / reverse

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() 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 / reversed

xs.reverse()                    # 原地反转,返回 None
list(reversed(xs))              # 返回新列表
xs[::-1]                        # 也是反转,最简洁

.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.

sum / min / max — 聚合

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 accept a key= function — common for "longest item" or "youngest user". Both raise ValueError on empty iterable unless you pass default=.

pop / remove / del

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 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.

bisect — 有序列表二分查找/插入

import bisect
xs = [1, 3, 5, 7, 9]
bisect.bisect_left(xs, 5)    # 2   5 应插入的最左位置
bisect.bisect_right(xs, 5)   # 3   最右位置
bisect.insort(xs, 4)         # 保持有序地插入 -> [1,3,4,5,7,9]
# 用 bisect 把分数转等级:
grades = "FDCBA"
i = bisect.bisect([60, 70, 80, 90], score)
grade = grades[i]

bisect does binary search on a SORTED list in O(log n). insort inserts while keeping order. Classic use: map a numeric score to a grade bucket.

heapq — 堆 / 优先队列 / TopK

import heapq
h = [5, 1, 3]
heapq.heapify(h)         # 原地建小顶堆
heapq.heappush(h, 2)
heapq.heappop(h)         # 1   弹最小
heapq.nlargest(3, nums)  # 最大的 3 个
heapq.nsmallest(3, nums) # 最小的 3 个
# 优先队列(用元组,第一个元素当优先级):
heapq.heappush(pq, (priority, task))
# 最大堆:存负数
heapq.heappush(maxh, -value)

heapq is a min-heap. heapify in place, heappush/heappop for the queue. nlargest/nsmallest beat sorting for top-k. For a max-heap, negate values.

collections.deque — 双端队列

from collections import deque
q = deque([1, 2, 3])
q.append(4)        # 右进
q.appendleft(0)    # 左进
q.pop()            # 右出
q.popleft()        # 左出   (list.pop(0) 是 O(n),deque 是 O(1))
q.rotate(1)        # 整体右转一位
# 定长滑动窗口:
window = deque(maxlen=3)
for x in stream:
    window.append(x)   # 超过 3 个自动挤掉最左

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.

dict.get — 安全取值

d = {"a": 1}
d["b"]              # KeyError
d.get("b")          # None
d.get("b", 0)       # 0   (默认值)
# 嵌套取值的常见写法:
d.get("user", {}).get("name", "anonymous")

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.

collections.Counter

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 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.

OrderedDict(3.7 后基本可弃用)

from collections import OrderedDict
od = OrderedDict()
od["a"] = 1
od["b"] = 2
od.move_to_end("a")       # 移到末尾
od.popitem(last=False)    # 弹首元素(普通 dict 没这俩方法)

Since 3.7 regular dict preserves order — most uses of OrderedDict can be replaced. Keep it only when you need move_to_end / popitem(last=False).

dict.pop / popitem / clear

d = {"a": 1, "b": 2, "c": 3}
d.pop("a")            # 1    删并返回值
d.pop("x", None)      # None  给默认值就不抛 KeyError
d.popitem()           # ('c', 3)   弹最后插入的 (3.7+ LIFO)
d.clear()             # 清空
# 安全地"取出并删除":
val = d.pop("key", "default")

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/values 集合运算

a = {"x": 1, "y": 2}
b = {"y": 3, "z": 4}
a.keys() & b.keys()    # {'y'}   共同键
a.keys() | b.keys()    # {'x','y','z'}
a.keys() - b.keys()    # {'x'}   只在 a 里
# keys() 是动态视图,dict 变它也变:
ks = a.keys()
a["w"] = 9
list(ks)               # 含 'w' 了

dict.keys()/items() are live VIEW objects that reflect later changes, and keys() supports set operators (& | -) directly — handy for diffing two dicts.

collections.ChainMap — 多字典叠加查找

from collections import ChainMap
defaults = {"color": "red", "size": "M"}
overrides = {"color": "blue"}
cfg = ChainMap(overrides, user, defaults)
cfg["color"]    # "blue"   按顺序第一个命中的赢
cfg["size"]     # "M"      回退到 defaults
# 不复制、不合并,按优先级链式查找
dict(cfg)       # 需要时再拍平成普通 dict

ChainMap searches multiple dicts in order without copying — first hit wins. Perfect for layered config (CLI args > env > file > defaults) without merging.

dict 按值排序 / 取 TopN

scores = {"lei": 90, "han": 85, "mei": 95}
# 按值降序:
sorted(scores.items(), key=lambda kv: kv[1], reverse=True)
# [('mei',95),('lei',90),('han',85)]
# 只要最高的人:
max(scores, key=scores.get)        # 'mei'
# Top 2:
import heapq
heapq.nlargest(2, scores.items(), key=lambda kv: kv[1])

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 去重(保留顺序与否)

# 简单去重,不保留顺序:
list(set([1, 2, 2, 3, 1]))     # 顺序不稳定
# 保留顺序去重(3.7+ dict 保序):
list(dict.fromkeys([1, 2, 2, 3, 1]))   # [1, 2, 3]

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.

set 原地更新 update / 差并交

a = {1, 2, 3}
a.update([3, 4, 5])           # a |= {...}   并入
a.intersection_update({2,4})  # a &= {...}   只留交集
a.difference_update({2})      # a -= {...}   减去
a.discard(99)                 # 不存在也不报错
a.pop()                       # 随机弹一个 (set 无序)
# 子集/超集判断:
{1, 2}.issubset({1, 2, 3})    # True
{1, 2, 3}.isdisjoint({4, 5})  # True  没有交集

The *_update methods (or |= &= -=) mutate a set in place. isdisjoint() checks for zero overlap without building the intersection. pop() removes an arbitrary element.

用 set 求两列表差异

old = ["a", "b", "c"]
new = ["b", "c", "d"]
added = set(new) - set(old)      # {'d'}    新增
removed = set(old) - set(new)    # {'a'}    删除
common = set(old) & set(new)     # {'b','c'} 保留
# 注意:会丢顺序和重复,只看"存在性"
# 要去重 + 保序:list(dict.fromkeys(...))

Diff two lists by converting to sets: new - old = added, old - new = removed, & = unchanged. Caveat: sets drop order and duplicates — membership only.

Tuple / dataclass (7)

tuple 基础与不可变性

t = (1, 2, 3)
t = 1, 2, 3            # 括号可省,逗号才是关键
single = (1,)          # 单元素元组要加逗号!
empty = ()
# 不可变,不能改:
# t[0] = 99  → TypeError
a, b, c = t            # 拆包

A tuple is defined by the COMMA, not the parens. Single-element tuple needs trailing comma: (1,) — (1) is just the int 1.

namedtuple — 轻量数据结构

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 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.

Function (13)

def 定义函数 / 默认参数

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

Default arguments are evaluated ONCE at def time — never use mutable defaults like [] or {}. Use None and create inside.

lambda 匿名函数

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 is single-expression only — no statements, no multi-line. Use it for short keys or callbacks; otherwise write a real def.

*args 与 **kwargs

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 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 hint 类型注解

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 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).

partial — 预绑定参数

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 creates a new callable with some args pre-filled. Cleaner than lambda when you just need argument binding.

仅位置参数 / (3.8+)

def div(a, b, /, *, precision=2):
    return round(a / b, precision)

div(10, 3)                    # ✅
div(10, 3, precision=4)       # ✅
# div(a=10, b=3)              # ❌ a,b 在 / 前,禁用关键字
# / 之前:仅位置   * 之后:仅关键字   中间:都行

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.

functools.singledispatch — 按类型分派

from functools import singledispatch
@singledispatch
def to_json(obj):
    raise TypeError(f"不支持 {type(obj)}")

@to_json.register
def _(obj: int):
    return str(obj)

@to_json.register
def _(obj: list):
    return "[" + ",".join(map(to_json, obj)) + "]"

to_json(42)        # "42"
to_json([1, 2])    # "[1,2]"

singledispatch turns a function into a generic that picks an implementation by the FIRST argument's type — Python's built-in single-dispatch polymorphism.

functools.reduce vs 内置 sum/math.prod

from functools import reduce
import math, operator
# 求和优先用内置 sum:
sum([1, 2, 3, 4])              # 10
# 求积(3.8+):
math.prod([1, 2, 3, 4])       # 24
# 通用折叠用 reduce + operator:
reduce(operator.mul, [1,2,3,4], 1)   # 24
reduce(operator.or_, [{1}, {2}, {3}])  # {1,2,3}  合并集合

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.

@property — 属性访问的优雅封装

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 turns a method into an attribute lookup. @prop.setter defines the write side. Use when validation or derivation is needed.

@classmethod / @staticmethod

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 takes cls (the class) — for factory methods. staticmethod takes nothing — for utility functions that live in the class namespace.

dunder methods — 让对象表现像内置类型

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)

Define __repr__ for debug print, __str__ for user print, __eq__/__hash__ for dict keys & sets, __lt__ for sorting, __add__ for + operator.

继承与 super

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!"

Inherit with class Sub(Base). Call super().method() to delegate to the parent. Multiple inheritance follows MRO (method resolution order).

@dataclass + frozen=True + slots

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 makes the instance immutable + hashable. slots=True (3.10+) uses __slots__ to save memory and prevent typos creating new attrs.

__enter__ / __exit__ 自定义上下文管理

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()

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.

__call__ — 让实例可调用

class Multiplier:
    def __init__(self, factor):
        self.factor = factor
    def __call__(self, x):
        return x * self.factor

double = Multiplier(2)
double(10)        # 20   像函数一样调用实例
# 适合"带状态的函数":计数器、累加器、配置好的转换器
callable(double)  # True

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.

functools.total_ordering — 补全比较

from functools import total_ordering
@total_ordering
class Version:
    def __init__(self, n): self.n = n
    def __eq__(self, o): return self.n == o.n
    def __lt__(self, o): return self.n < o.n
    # > >= <= 全部自动补出来

Version(1) < Version(2)    # True
Version(2) >= Version(1)   # True  (自动生成)

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 — 现代路径操作

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+) 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.

os / shutil — 文件系统操作

import os, shutil
os.path.exists("a.txt")
os.makedirs("a/b/c", exist_ok=True)   # 递归建目录
os.rename("old.txt", "new.txt")
os.remove("a.txt")                    # 删文件
os.listdir(".")
shutil.copy("src.txt", "dst.txt")     # 复制文件
shutil.copytree("src/", "dst/")       # 递归复制目录
shutil.move("a/", "b/")               # 移动
shutil.rmtree("dir/")                 # 递归删目录 (危险!)

os handles single-file ops (rename, remove, makedirs); shutil handles recursive/high-level ones (copytree, move, rmtree). Prefer pathlib for path manipulation.

glob — 通配符找文件

import glob
glob.glob("*.py")              # 当前目录 .py
glob.glob("src/**/*.py", recursive=True)   # 递归
glob.glob("data/[0-9]*.csv")   # 字符集
# pathlib 等价写法(更推荐):
from pathlib import Path
list(Path("src").rglob("*.py"))
list(Path(".").glob("*.txt"))

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 — 读 INI 配置

import configparser
cfg = configparser.ConfigParser()
cfg.read("app.ini", encoding="utf-8")
cfg["database"]["host"]            # 取值(都是字符串)
cfg.getint("database", "port")     # 转 int
cfg.getboolean("flags", "debug")   # 转 bool
cfg.sections()                     # 所有节
# 写:
cfg["new"] = {"key": "value"}
with open("app.ini", "w") as f:
    cfg.write(f)

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 与资源清理顺序

def read_config():
    f = open("config.txt")
    try:
        return f.read()      # return 后 finally 仍会执行
    finally:
        f.close()            # 保证关闭

# ⚠️ finally 里 return 会盖掉 try 里的 return / 吞掉异常:
def bad():
    try:
        return 1
    finally:
        return 2             # 实际返回 2,坑!

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.

logging.exception — 记录带堆栈

import logging
logging.basicConfig(level=logging.INFO)
log = logging.getLogger(__name__)
try:
    risky()
except Exception:
    log.exception("处理失败")    # 自动附上完整 traceback
    # 等价于 log.error("...", exc_info=True)
# traceback 模块手动取堆栈字符串:
import traceback
tb_str = traceback.format_exc()

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 / generator (10)

iter / next — 迭代器协议

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.i

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.

itertools 常用:chain / groupby / permutations

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 is the standard library iterator zoo. The #1 gotcha: groupby only groups CONSECUTIVE equal elements — you must sort first.

functools.reduce / lru_cache

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 folds a binary op across an iterable. @lru_cache memoizes — turns exponential recursion into linear. @cache (3.9+) is the shorthand.

zip_longest / takewhile / dropwhile

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]      跳到第一个 False

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 (8)

async / await 基础

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 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.

asyncio.create_task / wait / timeout

# 立刻调度,不等:
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 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 — 在协程里跑阻塞代码

import asyncio
# 把同步阻塞函数丢到线程池,不堵事件循环(3.9+):
async def main():
    result = await asyncio.to_thread(blocking_io_func, arg1, arg2)
    # 比如读大文件、调同步的 requests 库
    data = await asyncio.to_thread(open("big.txt").read)

# CPU 密集型用 run_in_executor + ProcessPoolExecutor
loop = asyncio.get_running_loop()
await loop.run_in_executor(pool, cpu_heavy, data)

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 vs 事件循环复用

import asyncio
# ✅ 顶层入口,跑完自动关循环:
asyncio.run(main())

# ❌ 已在协程里再调 run 会报错:
async def bad():
    asyncio.run(other())   # RuntimeError

# 协程里直接 await:
async def good():
    await other()

# 拿当前循环(3.10+):
loop = asyncio.get_running_loop()

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.

Typing (11)

typing 基础:List / Dict / Tuple / Set

# 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+ supports list[int] / dict[str, int] / set[X] directly. The typing module aliases (List, Dict) are still valid but no longer required.

Optional / Union / None

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] means T or None — not "optional argument". 3.10+ uses T | None / int | str instead of Optional / Union. Cleaner.

TypedDict — 结构化字典

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 报错:缺 age

TypedDict types dict shapes — useful for JSON-like data without writing a full class. 3.11+ adds NotRequired / Required for partial keys.

Protocol — 鸭子类型 + 静态检查

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+) is duck typing with static checking — any object with the right shape satisfies it, no inheritance needed. Python's answer to Go interfaces.

Generic / TypeVar — 泛型

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 declares a generic parameter. Generic[T] makes a class generic. Python 3.12+ has cleaner inline syntax: def f[T](...).

Literal / Final / Annotated

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 restricts to exact values — perfect for mode flags. Final marks constants. Annotated attaches metadata used by Pydantic, FastAPI, etc.

cast / overload — 类型断言与重载

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() 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 / assert_type(3.11+)

from typing import Self    # 3.11+
class Builder:
    def add(self, x) -> Self:    # 链式调用返回自身类型
        self._items.append(x)
        return self
    def build(self): ...

Builder().add(1).add(2).build()   # 类型推断正确

# 编译期断言类型(不影响运行):
from typing import assert_type
assert_type(parse("42"), int)

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 — 语义化类型别名

from typing import NewType
UserId = NewType("UserId", int)
OrderId = NewType("OrderId", int)

def get_user(uid: UserId): ...
uid = UserId(42)
get_user(uid)        # ✅
# get_user(42)       # mypy 报错:int 不是 UserId
# 运行时 UserId(42) 就是 42,零开销,只在类型层区分

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.

Common pitfalls (17)

可变默认参数(mutable default argument)

# ❌ 错误(所有调用共享同一个 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

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.

global / nonlocal 不写就 UnboundLocalError

count = 0
def inc():
    count += 1            # ❌ UnboundLocalError
                          # 因为函数里赋值了 count,Python 把它当本地变量

def inc():
    global count          # ✅ 显式声明
    count += 1

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.

整数除法 / vs //

7 / 2      # 3.5      (Python 3 一律返回 float)
7 // 2     # 3        整除
-7 // 2    # -4       向下取整(不是向 0 取整!)
divmod(7, 2)   # (3, 1)   商和余数一起

# 来自 Python 2 的代码可能假设 / 是整除,升 3 时小心

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).

except Exception 不要写裸 except

# ❌ 吞掉 KeyboardInterrupt 和 SystemExit
try:
    risky()
except:
    pass

# ✅ 抓 Exception 就够了
try:
    risky()
except Exception as e:
    log.exception(e)

Bare `except:` catches BaseException — including KeyboardInterrupt and SystemExit. You can't Ctrl+C out. Use `except Exception:` to catch all normal errors.

浮点数精度与比较

0.1 + 0.2 == 0.3        # False!
0.1 + 0.2               # 0.30000000000000004
# ✅ 用 math.isclose 比较:
import math
math.isclose(0.1 + 0.2, 0.3)   # True
# 算钱用 Decimal:
from decimal import Decimal
Decimal("0.1") + Decimal("0.2")   # Decimal('0.3')

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.

列表乘法的共享引用坑

# ❌ 二维数组初始化的经典坑:
grid = [[0] * 3] * 3        # 三行指向同一个 list!
grid[0][0] = 1
print(grid)                 # [[1,0,0],[1,0,0],[1,0,0]]  全变了

# ✅ 用推导式各建各的:
grid = [[0] * 3 for _ in range(3)]
grid[0][0] = 1              # 只改第一行

[[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.

sort 比较不同类型抛 TypeError

# ❌ Python 3 不能比较 int 和 str:
sorted([1, "a", 2])     # TypeError
[3, None, 1].sort()     # TypeError (None 不能比)
# ✅ 给 key 统一比较维度:
sorted(mixed, key=str)              # 都转字符串比
sorted(items, key=lambda x: (x is None, x))   # None 排最后

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.

可变对象做集合元素 / dict 键

# ❌ list 不可哈希,不能当 dict 键或进 set:
{[1, 2]: "v"}          # TypeError: unhashable type: 'list'
{[1, 2], [3, 4]}       # TypeError
# ✅ 换成元组(不可变可哈希):
{(1, 2): "v"}          # OK
{(1, 2), (3, 4)}       # OK
# 自定义类当键要实现 __hash__ + __eq__,且哈希依赖的字段不能变

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.

模块级可变状态与导入副作用

# 模块只在第一次 import 时执行一次,结果被缓存:
# config.py
loaded_at = time.time()    # 整个进程只算一次
cache = {}                 # 全模块共享,跨调用累积

# 坑:循环导入 / 改不到的"重新加载"
import importlib
importlib.reload(config)   # 极少用,调试除外
# 别在模块顶层做重 IO / 网络请求,import 时就触发了

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.

  1. 1 JSON Formatter & Validator Format, validate, and minify JSON instantly — right in your browser. Open
  2. 2 SQL Cheatsheet SQL cheat sheet — 100+ statements covering SELECT, JOIN, window functions, indexing, MySQL/PostgreSQL/SQLite differences. Open
  3. 3 curl Cheatsheet curl cheat sheet — 80+ curl commands for GET/POST/auth/upload/download/SSL/proxy, with real examples and pitfalls. Open

Real-world use cases

  • Recall the dict.get / setdefault pattern mid-task

    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

Tool combos

Folks in your role tend to reach for these alongside this tool.

Made by Toolora · 100% client-side · Updated 2026-07-02