楼层: 首页/ 软件技术/ Python 基础/ 常见坑与最佳实践
13

常见坑与最佳实践

Pitfalls & Best Practices

这些坑,每个 Python 新手几乎都会踩一遍。提前看一眼,省得以后 debug 到凌晨三点。

坑 1:可变默认参数

# 危险写法:默认列表只在函数定义时创建一次! def f(x=[]): x.append(1) return x print(f()) # [1] print(f()) # [1, 1] ← 你以为每次都是空列表?错! print(f()) # [1, 1, 1] # 正确写法:默认用 None,函数内部再创建 def f(x=None): if x is None: x = [] x.append(1) return x

坑 2:循环变量闭包

# 坑:lambda 里引用的 i 是"活的",循环结束后才读值 funcs = [lambda: i for i in range(3)] print([f() for f in funcs]) # [2, 2, 2] 不是 [0,1,2]! # 正确:用默认参数在定义时固定值 funcs = [lambda i=i: i for i in range(3)] print([f() for f in funcs]) # [0, 1, 2]

坑 3:浅拷贝 vs 深拷贝

import copy a = [[1, 2], [3, 4]] b = a.copy() # 浅拷贝:外层新,内层还是共用 b[0][0] = 99 print(a) # [[99, 2], [3, 4]] ← a 也被改了! c = copy.deepcopy(a) # 深拷贝:完全独立 c[0][0] = 0 print(a) # [[99, 2], [3, 4]] 不受影响

坑 4:浮点精度

print(0.1 + 0.2) # 0.30000000000000004,不是 0.3 print(0.1 + 0.2 == 0.3) # False # 财务计算用 decimal from decimal import Decimal print(Decimal("0.1") + Decimal("0.2")) # 0.3

坑 5:用 is 比较字符串和数字

# is 比的是"是不是同一个对象",不是"值相不相等" a = [1, 2] b = [1, 2] print(a == b) # True 值相等 print(a is b) # False 不是同一个对象 # 比较值永远用 ==;判断 None 用 x is None
本章面试题 · 内存与垃圾回收

1.(概念题)Python 的内存管理靠哪两招?什么是引用计数?

查看答案

答案:CPython 主要靠引用计数——每个对象记录有多少个引用指向它,引用归零就立即销毁;再加分代垃圾回收(GC)专门解决循环引用(a 引用 b、b 引用 a,计数都不归零)。sys.getrefcount(x) 能看引用数(注意它自己也会+1)。

2.(概念题)深拷贝和浅拷贝的区别?下面代码 a 变了吗?

import copy
a = [[1,2],[3,4]]
b = copy.copy(a)
b[0][0] = 99

查看答案

答案:浅拷贝只拷外层容器,内层元素还是共享引用;深拷贝递归拷到底,完全独立。这里 b[0][0]=99 改的是内层那个共用列表,a 也会变成 [[99,2],[3,4]]。要彻底独立用 copy.deepcopy(a)。

3.(概念题)什么是循环引用?为什么引用计数管不了它?GC 怎么解决?

查看答案

答案:对象 a 里存了 b,b 里又存了 a,两者引用计数都 ≥1,哪怕外部再也访问不到它们,计数也永不归零——引用计数漏网之鱼。Python 的分代 GC 定期扫描"容器对象"(list/dict/对象),把"只被彼此引用、外部没人指向"的孤儿圈销毁。gc.collect() 可手动触发。

4.(代码题)下面代码内存会泄漏吗?为什么?

cache = {}
def add(key, obj):
    cache[key] = obj
# 用完从不删,cache 越攒越大

查看答案

答案:会"逻辑上的内存膨胀"(虽然不是 C 那种 leak,但效果一样):全局 dict 一直强引用对象,GC 永远回收不了。修法:① 用完 del cache[key];② 用 weakref.WeakKeyDictionary 或 weakref.finalize 做弱引用缓存——没人用了自动清。

5.(思考题)为什么 Python 里整数、小字符串会"缓存"?is 为什么不能随便用来比值?

查看答案

答案:CPython 为了省内存,会缓存 -5 到 256 的小整数和一些短字符串,让它们在解释器里是同一个对象。但这只是实现细节,跨模块/大整数就不保证了。is 比的是"同一个对象",== 比的是"值相等"。比值永远用 ==,判断 None 才用 is None。

记
最佳实践清单

① 变量函数用 snake_case,类用 PascalCase,常量用 UPPER_SNAKE。

② 文件统一 encoding="utf-8",别让中文变乱码。

③ 能用标准库就别装第三方;装了就写进 requirements / pyproject。

④ 函数别超过 50 行,超过就拆。一个函数干一件事。

⑤ 写 docstring,三个月后你会感谢自己。