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