Python 的语法很友好,但这并不意味着它没有坑。可变默认参数、拷贝语义、引用共享、生成器和列表的内存差异、GIL 的影响,这些问题一旦在实际项目里出现,往往都不是一句“语法记错了”能带过的。
这一节我们会把这些高频陷阱集中放在一起,不只是为了避坑,更是为了帮助你建立一种更底层的理解:代码为什么会这样表现,优化又应该从哪里开始。
可变默认参数为什么危险?
这个问题我们前面讲函数时已经遇到过,但它值得在“性能与陷阱”章节里再次强调,因为它本质上暴露的是对象共享和生命周期问题。
可变默认参数危险,不只是因为“结果会串”,更因为它会制造隐式共享状态,让函数行为变得难以推断。
这类问题最可怕的地方,不在于难修,而在于它在小样例里很容易隐藏起来,等到真实项目里才突然爆出来。
深拷贝、浅拷贝与引用共享
Python 里变量绑定的是对象引用,而不是值本体。这意味着当你复制一个容器时,很容易只复制了外层结构,而内部对象仍然共享。
所以浅拷贝适合:
- 结构简单;
- 内部元素不需要独立修改;
- 明确知道共享是可接受的场景。
而一旦内部存在嵌套可变对象,又需要真正独立副本,就要考虑深拷贝。
理解这件事的意义在于,很多“为什么改了 A,B 也跟着变了”的问题,根本不是 Python 古怪,而是对象共享本来就是这样发生的。
列表、生成器与内存占用差异
当你写:
[x * 2 for x in range(1000000)]和:
(x * 2 for x in range(1000000))虽然表面只差一个括号,但内存行为完全不同。
前者会立刻生成完整列表,后者则是生成器,按需产出结果。
所以性能问题并不总是“算法太差”,很多时候只是数据处理方式和真实需求不匹配。你明明只需要逐步消费数据,却先把全量结果都堆进了内存。
GIL 到底影响了什么?
GIL 经常被提及,但也经常被误解。
你不用把它理解成“Python 不能并发”,更准确地说,它会影响同一进程内多个线程并行执行 Python 字节码的方式。
这会让 CPU 密集型任务在线程模型下很难真正利用多核优势,但对 I/O 密集型任务来说,线程依然可能是合适方案。
所以 GIL 的关键不是“它好不好”,而是提醒你:并发模型选择必须基于任务特征,而不是只看概念名字。
常见性能优化思路
先测量,再优化
性能优化里最危险的事,就是凭感觉乱改。
很多时候真正慢的地方和你以为的地方根本不是同一处。所以优化前先测量,是非常重要的工程纪律。你至少要知道:
- 时间花在哪;
- 内存消耗在哪;
- 是算法问题、数据结构问题,还是 I/O 边界问题。
善用内置能力与标准库
Python 很多性能上的“低成本提升”,并不是去写复杂技巧,而是:
- 用对数据结构;
- 用对标准库;
- 避免不必要中间结果;
- 把高频循环内能外提的动作外提。
这类优化比起炫技式微调,往往更稳也更容易维护。
总结与预告
这一节我们把一批常见但很容易被忽略的问题集中摊开了来看。很多“奇怪行为”背后其实并不奇怪,只是我们此前还没有站到足够底层的角度去理解 Python 的对象模型和执行方式。
下一节我们会顺势进入并发主题,先把线程、进程和协程之间的边界与选择逻辑讲清楚。