4. Python 陷阱
本章汇总 Python 开发中最常见的陷阱和易错点,帮助你避免踩坑。
4.5. 陷阱分类
🐛 常见错误
可变默认参数、作用域问题、浮点数精度、字符串编码等。
⚡ 性能陷阱
低效循环、不当数据结构选择、内存复制、过早优化等。
💾 内存问题
循环引用、大对象处理、生成器误用、缓存泄漏等。
🔍 调试技巧
日志最佳实践、性能分析、内存分析、常用工具等。
4.6. 快速参考:Top 10 陷阱
4.6.1. 1. 可变默认参数
# ❌ 错误
def append_to(item, lst=[]):
lst.append(item)
return lst
print(append_to(1)) # [1]
print(append_to(2)) # [1, 2] 不是 [2]!
# ✅ 正确
def append_to(item, lst=None):
if lst is None:
lst = []
lst.append(item)
return lst
4.6.2. 2. 循环中的闭包
# ❌ 错误
funcs = [lambda: i for i in range(3)]
print([f() for f in funcs]) # [2, 2, 2]
# ✅ 正确
funcs = [lambda i=i: i for i in range(3)]
print([f() for f in funcs]) # [0, 1, 2]
4.6.3. 3. is vs ==
# ❌ 错误
a = 256
b = 256
print(a is b) # True (小整数缓存)
a = 257
b = 257
print(a is b) # False!
# ✅ 正确:用 == 比较值
print(a == b) # True
# is 只用于 None 比较
if x is None: pass
4.6.4. 4. 修改迭代中的列表
# ❌ 错误
items = [1, 2, 3, 4, 5]
for item in items:
if item % 2 == 0:
items.remove(item) # 跳过元素!
print(items) # [1, 3, 5] 可能不是预期
# ✅ 正确
items = [1, 2, 3, 4, 5]
items = [x for x in items if x % 2 != 0]
4.6.5. 5. 字典迭代时修改
# ❌ 错误
d = {'a': 1, 'b': 2, 'c': 3}
for k in d:
if d[k] == 2:
del d[k] # RuntimeError!
# ✅ 正确
d = {k: v for k, v in d.items() if v != 2}
# 或
for k in list(d.keys()): # 复制 keys
if d[k] == 2:
del d[k]
4.6.6. 6. 浮点数比较
# ❌ 错误
print(0.1 + 0.2 == 0.3) # False!
# ✅ 正确
import math
print(math.isclose(0.1 + 0.2, 0.3)) # True
# 或使用 decimal
from decimal import Decimal
print(Decimal('0.1') + Decimal('0.2') == Decimal('0.3')) # True
4.6.7. 7. 类属性共享
# ❌ 错误
class Student:
grades = [] # 类属性
def add_grade(self, grade):
self.grades.append(grade)
s1 = Student()
s2 = Student()
s1.add_grade(90)
print(s2.grades) # [90] 被共享了!
# ✅ 正确
class Student:
def __init__(self):
self.grades = [] # 实例属性
4.6.8. 8. 异常中的 return
# ⚠️ 容易混淆
def confusing():
try:
return "try"
finally:
return "finally" # finally 的 return 覆盖 try 的!
print(confusing()) # "finally"
# ✅ 最佳实践:finally 中不要 return
def clear():
try:
return "try"
finally:
# 只做清理,不要 return
pass
4.6.9. 9. GIL 与线程
# ⚠️ CPU 密集型任务,多线程不会更快
import threading
counter = 0
def increment():
global counter
for _ in range(1000000):
counter += 1 # 不是原子操作!
threads = [threading.Thread(target=increment) for _ in range(2)]
for t in threads:
t.start()
for t in threads:
t.join()
print(counter) # 小于 2000000!
# ✅ CPU 密集型用多进程
from multiprocessing import Pool, Value
4.6.10. 10. 作用域陷阱
# ❌ 错误
x = 10
def foo():
print(x) # UnboundLocalError!
x = 20
# ✅ 正确
def foo():
global x # 或 nonlocal(嵌套函数)
print(x)
x = 20
# 或者不修改全局变量(推荐)
def foo(x):
return x + 10
4.7. 调试快速参考
# 1. 打印变量和类型
print(f"{var=}, {type(var)=}")
# 2. 使用断点
breakpoint() # Python 3.7+
# 3. 追踪函数调用
import traceback
traceback.print_stack()
# 4. 检查对象
import inspect
print(inspect.getmembers(obj))
# 5. 内存使用
import sys
print(sys.getsizeof(obj))
4.8. 性能检查快速参考
# 1. 时间测量
import time
start = time.perf_counter()
# ... 代码 ...
print(f"Took {time.perf_counter() - start:.4f}s")
# 2. 使用 timeit
import timeit
print(timeit.timeit('"-".join(str(n) for n in range(100))', number=10000))
# 3. cProfile
import cProfile
cProfile.run('my_function()')
# 4. 内存分析
# pip install memory_profiler
# @profile
# def my_function(): ...
# python -m memory_profiler script.py