对于刚接触Python的开发者来说,基础语法看似简单,但实际编码中隐藏着大量“看着对、实则错”的隐性机制。这些坑不仅让线上代码崩溃,更是面试中的高频考题。下面整理了20个最常出现的Python陷阱,每个都附有错误示例、原因分析和正确写法,覆盖默认参数、深浅拷贝、循环变量泄露、整数缓存、GIL(全局解释器锁)、异常捕获、文件操作等核心知识点。
- 1. 默认参数为可变对象
- 错误写法:
- def func(arr=[]):
- arr.append(1)
- print(arr)
- func()
- func() # 输出 [1] [1, 1],列表不断累加
- 原因:默认参数在函数定义时仅创建一次,后续调用复用同一对象。
- 正确写法:
- def func(arr=None):
- if arr is None:
- arr = []
- arr.append(1)
- print(arr)
复制代码- 2. for循环变量泄露
- 错误示例:
- for i in range(3):
- pass
- print(i) # 输出2,循环结束后i依然存在
- 原因:Python的for循环不会创建独立作用域,循环变量会泄露到外部命名空间。
- 解决:避免外部复用同名变量,或将逻辑抽取到单独函数中。
复制代码- 3. 遍历列表时删除元素
- 错误写法:
- lst = [1, 2, 3, 4]
- for i in lst:
- if i % 2 == 0:
- lst.remove(i)
- print(lst) # 可能漏删或索引错乱
- 原因:遍历过程中列表长度变化,导致后续元素索引移位。
- 正确写法:用列表推导式创建新列表:
- lst = [x for x in lst if x % 2 != 0]
复制代码- 4. == 与 is 混用
- 规则:== 比较值是否相等,is 比较内存地址是否相同。
- 陷阱:小整数(-5~256)和短字符串可能被缓存,导致 is 结果与预期不符。
- 建议:值比较用==,判断None或单例时用is。
复制代码- 5. 整数缓存范围(-5~256)
- 交互式环境示例:
- a = 256; b = 256; print(a is b) # True
- c = 257; d = 257; print(c is d) # False(超出缓存范围)
- 注意:在.py文件中因编译优化行为可能略有不同,但仍不建议依赖is判断整数值。
复制代码- 6. 字符串拼接使用大量 +
- 错误:'a' + 'b' + 'c' ... 产生大量临时字符串对象。
- 原因:字符串不可变,每次拼接都创建新对象。
- 正确做法:使用 ''.join(iterable) 方法。
复制代码- 7. 字典直接取值导致KeyError
- 错误:d = {'name': 'Tom'}; print(d['age']) # 抛出KeyError
- 正确:d.get('age', 18) # 返回默认值18,不会崩溃
复制代码- 8. 浅拷贝导致嵌套列表联动
- 示例:
- a = [[1, 2], 3]
- b = list(a)
- b[0][0] = 99
- print(a) # [[99, 2], 3] 原数据被修改
- 原因:只拷贝外层对象,内层列表仍为引用。
- 解决:对嵌套结构使用 copy.deepcopy()。
复制代码- 9. 裸 except 捕获所有异常
- 错误:try: 1/0 except: pass # 隐藏了ZeroDivisionError
- 危害:导致排错困难,线上可能吞掉关键错误。
- 正确:永远捕获具体异常类型,如 except ValueError:。
复制代码- 10. 文件 open 后不关闭
- 资源泄露,可能导致句柄耗尽、文件无法删除。
- 正确:使用 with open('file.txt') as f: 上下文管理器自动关闭。
复制代码- 11. 全局变量修改未声明 global
- num = 10
- def f():
- num = 20 # 创建局部变量,不修改全局
- f()
- print(num) # 10
- 如需修改全局变量,函数内必须加 global num。
复制代码- 12. 闭包延迟绑定(面试高频)
- 示例:
- funcs = []
- for i in range(3):
- def f():
- print(i)
- funcs.append(f)
- funcs[0]() # 输出2,不是0
- funcs[1]() # 输出2
- 原因:闭包捕获的是变量i的引用,循环结束后i为2。
- 解决:使用默认参数立即绑定:
- def f(i=i):
- print(i)
复制代码- 13. True/False 本质是 1/0
- print(True == 1) # True
- 判断布尔值时不要与数字混用,避免逻辑混淆。
复制代码- 14. if 多条件连续赋值错误
- 错误:if a == 1 or 2: # 永远成立,因为2为真
- 原因:误以为 or 需要完整条件,实际上2被视为布尔值True。
- 正确:if a == 1 or a == 2:
复制代码- 15. 空列表/字典的布尔判断
- 空列表[]、空字典{}、空字符串''、0、None 在条件判断中均为False。
- 容易引起逻辑误判,例如:if not my_list: 时不要假设变量非空。
复制代码- 16. 递归深度溢出
- Python默认递归深度约1000,超出则抛出RecursionError。
- 优先考虑迭代实现;若必须递归,可设置 sys.setrecursionlimit(),但风险较大。
复制代码- 17. time.sleep 阻塞协程
- 在asyncio协程中使用 time.sleep(n) 会阻塞整个事件循环。
- 正确:await asyncio.sleep(n)
复制代码- 18. 多线程高估效率(GIL坑)
- Python的GIL导致同一时间只有一个线程执行字节码,CPU密集型任务多线程反而更慢。
- 正确:CPU密集任务使用 multiprocessing 多进程。
复制代码- 19. 函数返回多个值默认元组
- def test(): return 1, 2
- res = test()
- print(type(res)) # tuple
- 解包时需注意元素个数匹配,否则引发ValueError。
复制代码- 20. 编码不指定导致乱码
- Windows默认GBK,Linux默认UTF-8,跨平台时文件读写可能乱码。
- 强制指定编码:
- with open('file.txt', encoding='utf-8') as f:
复制代码
以上20个陷阱是Python新手最常遇到的雷区,覆盖了从基础语法到并发编程的多个层面。理解其背后的本质——可变对象缓存、作用域规则、对象模型等——比死记硬背更重要。面试中这些知识点也反复出现,建议结合代码练习彻底吃透。 |