为什么 Python 代码在函数中运行得更快
当谈到编程效率和性能优化时,Python 常常被调侃为'慢如蜗牛'。有趣的是,在实际开发中发现,Python 代码在函数内部运行往往比在全局范围内(脚本顶层)运行要快得多。很多开发者可能会疑惑:为什么在函数中运行的 Python 代码速度更快?
本文解析了 Python 代码在函数中运行比全局范围更快的原因。核心在于变量存储机制:局部变量存储在固定长度数组中,通过索引直接访问;而全局变量存储在字典中,需进行哈希查找,开销更大。文章通过 dis 模块展示字节码差异(STORE_FAST vs STORE_NAME),利用 timeit 和 cProfile 进行基准测试验证,并给出了使用局部变量、内置函数等优化建议。

当谈到编程效率和性能优化时,Python 常常被调侃为'慢如蜗牛'。有趣的是,在实际开发中发现,Python 代码在函数内部运行往往比在全局范围内(脚本顶层)运行要快得多。很多开发者可能会疑惑:为什么在函数中运行的 Python 代码速度更快?
要理解这一现象,首先需要了解 Python 是如何执行代码的。Python 是一种解释型语言,它会逐行读取并执行代码。当运行一个 Python 程序时,首先将源代码编译成字节码(一种更接近机器码的中间语言),然后 Python 解释器执行这些字节码。
需要注意的是,Python 解释器是一个执行字节码的虚拟机。默认的 Python 解释器是 CPython,它是用 C 编写的。此外还有 Jython(用 Java 编写)、IronPython(用于 .NET)和 PyPy(用 Python 和 C 编写)等其他实现。
Python 中的 dis 模块可以将函数分解为字节码指令,帮助我们查看底层执行细节。
我们来通过一个简单的例子来对比。定义一个函数 my_function,函数内部包含一个 for 循环。
def my_function():
for i in range(100):
pass
编译该函数时,生成的字节码中包含关键指令 STORE_FAST,用于存储循环变量 i。这表示变量存储在局部作用域的快速数组中。
现在,我们将同样的 for 循环放在 Python 脚本的顶层(全局范围内):
for i in range(100):
pass
再次查看其字节码,可以看到关键指令变成了 STORE_NAME,而不是 STORE_FAST。
字节码 STORE_FAST 比 STORE_NAME 快,原因在于变量存储机制的不同:
因此,减少全局变量的使用,尽量使用局部变量,是提升 Python 代码性能的关键策略之一。
为了量化这种差异,我们可以使用 Python 内置的 timeit 模块对代码进行计时。
def factorial(n):
result = 1
for i in range(1, n + 1):
result *= i
return result
print(timeit.timeit('factorial(10)', setup='from __main__ import factorial', number=10000))
result = 1
for i in range(1, 11):
result *= i
print(timeit.timeit('result', 'from __main__ import result', number=10000))
注意:这两段代码最好不要放在同一脚本中同时运行,因为
benchmark()函数在执行时间上增加了一些开销,且全局代码在内部进行了特定优化。分开单独运行更能反映真实差异。
测试结果通常显示,函数代码的执行速度明显优于全局作用域代码。
Python 提供了一个 cProfile 内置模块,用于分析代码的性能瓶颈。
让我们用它来分析一个新例子:在局部和全局范围内计算平方和。
# 局部版本
def sum_of_squares_local(n):
total = 0
for i in range(n):
total += i * i
return total
# 全局版本
total = 0
for i in range(1000):
total += i * i
从性能分析结果中可以看到,函数代码在执行时间方面比全局代码更有效,主要消耗在函数调用开销上,但变量访问开销显著降低。
既然我们知道 Python 代码在函数中运行往往比在全局范围内运行要快得多,那么如何进一步优化呢?
避免在循环中使用全局变量。如果必须在循环中使用某个值,请将其赋值给局部变量。
# 不推荐:每次循环都查找全局变量
data = [1, 2, 3]
for item in data:
print(item)
# 推荐:使用局部变量
def process_data():
local_data = [1, 2, 3]
for item in local_data:
print(item)
尽可能使用内置函数和标准库。Python 的许多内置函数是用 C 实现的,比纯 Python 代码快得多。
sum 函数要比自己编写循环速度快得多。# 推荐
numbers = [1, 2, 3, 4, 5]
print(sum(numbers))
# 不推荐
print(sum([x for x in numbers])) # 额外生成列表
在类方法或函数中,频繁访问对象属性也会带来开销。可以将常用属性缓存到局部变量中。
Python 代码在函数中运行更快的根本原因在于变量查找机制的差异:局部变量通过数组索引快速访问,而全局变量需要通过哈希表查找。通过遵循最佳实践,如封装代码到函数、使用局部变量以及利用高性能的内置库,可以显著提升 Python 程序的执行效率。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online