常见坑与最佳实践
这些坑,每个 Python 新手几乎都会踩一遍。提前看一眼,省得以后 debug 到凌晨三点。
坑 1:可变默认参数
坑 2:循环变量闭包
坑 3:浅拷贝 vs 深拷贝
坑 4:浮点精度
坑 5:用 is 比较字符串和数字
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,三个月后你会感谢自己。