跳到主要内容 9 个提升 Python 代码运行效率的实用技巧 | 极客日志
Python 算法
9 个提升 Python 代码运行效率的实用技巧 本文详细介绍了 9 个提升 Python 代码运行效率的实用技巧。内容包括字符串拼接应优先使用 join 而非循环累加、创建列表时使用字面量 [] 比 list() 更快、成员关系测试中集合 set 比列表 list 效率高、使用推导式替代普通 for 循环、访问局部变量优于全局变量或对象属性、优先使用内置模块如 collections.Counter、利用 functools.cache 缓存避免重复计算、以及按需导入模块以减少启动时间。文章还强调了性能分析的重要性,建议先使用 cProfile 等工具定位瓶颈,并指出算法优化往往比微优化更有效,同时提醒开发者在追求性能时需兼顾代码可读性。
林间仙子 发布于 2025/2/6 更新于 2026/4/20 1 浏览我们经常听到'Python 太慢了'、'Python 性能不行'这样的观点。虽然 Python 作为解释型语言,在执行速度上确实不如 C/C++ 等编译型语言,但通过掌握一些编程技巧和最佳实践,就能大幅提升 Python 的运行速度。
本文将详细介绍 9 个实用的 Python 性能优化技巧,帮助你在编写代码时做出更高效的决策。
1. 字符串拼接的技巧 如果有大量字符串等待处理,字符串连接将成为 Python 的瓶颈。
一般来讲,Python 中有两种主要的字符串拼接方式:
使用 join() 函数将字符串列表合并为一个字符串
使用 + 或 += 符号将每个字符串逐个添加
mylist = ["Yang" , "Zhou" , "is" , "writing" ]
def concat_plus ():
result = ""
for word in mylist:
result += word + " "
return result
def concat_join ():
return " " .join(mylist)
def concat_directly ():
return "Yang" + "Zhou" + "is" + "writing"
import timeit
print (timeit.timeit(concat_plus, number=10000 ))
print (timeit.timeit(concat_join, number=10000 ))
print (timeit.timeit(concat_directly, number=10000 ))
如上所示,对于拼接字符串列表,join() 方法比在 for 循环中逐个添加字符串更快。
原因分析:
一方面,字符串是 Python 中的不可变数据(Immutable)。每次执行 += 操作都会导致创建一个新字符串并复制旧字符串的内容,这会产生巨大的内存分配和拷贝开销。
另一方面,.join() 方法是专门为连接字符串序列而优化的。它预先计算结果字符串的大小,然后一次性构建它。因此,它避免了与循环中 += 操作相关的多次内存分配开销。
但是,我们发现最快其实是直接用 + 拼接常量字符串,这是因为:
Python 解释器可以在编译时优化字符串的连接,将它们转换为单个字符串(String Interning)。
由于所有字符串在编译时都是已知的,没有循环迭代或函数调用,所以它是一个非常高效的操作。
总结: 如果需要拼接字符串列表,请选择 join();如果直接拼接已知常量字符串,只需使用 + 即可。
2. 创建列表的技巧 import timeit
print (timeit.timeit('[]' , number=10 ** 7 ))
print (timeit.timeit(list , number=10 ** 7 ))
结果表明,执行 list() 函数比直接使用 [] 要慢。
原理: [] 是字面语法(literal syntax),由字节码直接支持;而 list() 是构造函数调用,需要额外的函数查找和调用开销。毫无疑问,调用函数需要更多时间。
同理,在创建字典时,我们也应该利用 {} 而不是 dict()。
3. 成员关系测试的技巧 import timeit
large_dataset = range (100000 )
search_element = 2077
large_list = list (large_dataset)
large_set = set (large_dataset)
def list_membership_test ():
return search_element in large_list
def set_membership_test ():
return search_element in large_set
print (timeit.timeit(list_membership_test, number=1000 ))
print (timeit.timeit(set_membership_test, number=1000 ))
如上面的代码所示,集合中的成员关系测试比列表中的成员关系测试要快得多(相差约 300 倍)。
在 Python 列表中,成员关系测试 (element in list) 是通过遍历每个元素来完成的,直到找到所需的元素或到达列表的末尾。因此,此操作的时间复杂度为 O(n)。
Python 中的集合(set)是作为哈希表实现的。在检查成员资格 (element in set) 时,Python 使用哈希机制,其平均时间复杂度为 O(1)。
建议: 这里的技巧重点是在编写程序时仔细考虑底层数据结构。利用正确的数据结构可以显著加快我们的代码速度。
4. 使用推导式而不是 for 循环 Python 中有四种类型的推导式:列表、字典、集合和生成器。它们不仅为创建相对数据结构提供了更简洁的语法,而且比使用 for 循环具有更好的性能。
因为它们在 Python 的 C 实现中进行了优化,减少了字节码的执行次数。
import timeit
def generate_squares_for_loop ():
squares = []
for i in range (1000 ):
squares.append(i * i)
return squares
def generate_squares_comprehension ():
return [i * i for i in range (1000 )]
print (timeit.timeit(generate_squares_for_loop, number=10000 ))
print (timeit.timeit(generate_squares_comprehension, number=10000 ))
上面的代码是列表推导式和 for 循环之间的简单速度比较。如结果所示,列表推导式速度更快。这是因为推导式在内部使用了优化的迭代逻辑,避免了显式的 append 方法调用开销。
5. 访问局部变量速度更快 在 Python 中,访问局部变量比访问全局变量或对象的属性更快。
import timeit
class Example :
def __init__ (self ):
self .value = 0
obj = Example()
def test_dot_notation ():
for _ in range (1000 ):
obj.value += 1
def test_local_variable ():
value = obj.value
for _ in range (1000 ):
value += 1
obj.value = value
print (timeit.timeit(test_dot_notation, number=1000 ))
print (timeit.timeit(test_local_variable, number=1000 ))
原理: 当编译一个函数时,它内部的局部变量是已知的,存储在栈帧中,访问速度快。而访问对象属性(如 obj.value)需要通过 __getattribute__ 等方法进行动态查找,涉及哈希查找和协议调用,开销较大。
6. 优先考虑内置模块和库 当我们讨论 Python 的时候,通常指的是 CPython,因为 CPython 是 Python 语言的默认和使用最广泛的实现。
考虑到它的大多数内置模块和库都是用 C 语言编写的,C 语言是一种更快、更低级的语言,我们应该利用它的内置库,避免重复造轮子。
import timeit
import random
from collections import Counter
def count_frequency_custom (lst ):
frequency = {}
for item in lst:
if item in frequency:
frequency[item] += 1
else :
frequency[item] = 1
return frequency
def count_frequency_builtin (lst ):
return Counter(lst)
large_list = [random.randint(0 , 100 ) for _ in range (1000 )]
print (timeit.timeit(lambda : count_frequency_custom(large_list), number=100 ))
print (timeit.timeit(lambda : count_frequency_builtin(large_list), number=100 ))
上面的程序比较了计算列表中元素频率的两种方法。正如我们所看到的,利用 collections 模块的内置计数器比我们自己编写 for 循环更快、更简洁、更好。这是因为 Counter 是用 C 实现的底层逻辑。
7. 使用缓存装饰器 幸运的是,在大多数情况下,我们不需要编写自己的缓存处理代码,因为 Python 提供了一个开箱即用的装饰器 — @functools.cache (Python 3.9+) 或 @lru_cache。
例如,以下代码将执行两个斐波那契数生成函数,一个具有缓存装饰器,但另一个没有:
import timeit
import functools
def fibonacci (n ):
if n in (0 , 1 ):
return n
return fibonacci(n - 1 ) + fibonacci(n - 2 )
@functools.cache
def fibonacci_cached (n ):
if n in (0 , 1 ):
return n
return fibonacci_cached(n - 1 ) + fibonacci_cached(n - 2 )
print (timeit.timeit(lambda : fibonacci(30 ), number=1 ))
print (timeit.timeit(lambda : fibonacci_cached(30 ), number=1 ))
可以看到 functools.cache 装饰器如何使我们的代码运行得更快。
缓存版本的速度明显更快,因为它缓存了先前计算的结果。因此,它只计算每个斐波那契数一次,并从缓存中检索具有相同参数的后续调用。这对于递归算法或纯函数特别有效。
8. while 1 VS while True 如果要创建无限 while 循环,我们可以使用 while True 或 while 1。
它们的性能差异通常可以忽略不计。但有趣的是,while 1 稍微快一点。
这是因为 1 是整数字面量,而 True 是一个全局名称,需要在 Python 的全局作用域中查找。所以 1 的开销很小。
import timeit
def loop_with_true ():
i = 0
while True :
if i >= 1000 :
break
i += 1
def loop_with_one ():
i = 0
while 1 :
if i >= 1000 :
break
i += 1
print (timeit.timeit(loop_with_true, number=10000 ))
print (timeit.timeit(loop_with_one, number=10000 ))
正如我们所看到的,确实 while 1 稍微快一些。
然而,现代 Python 解释器 (如 CPython) 是高度优化的,这种差异通常是微不足道的。所以我们不需要担心这个可以忽略不计的差异。更不用说 while True 比 while 1 可读性更好。在工程实践中,推荐优先保证代码的可读性。
9. 按需导入 Python 模块 在 Python 脚本开头导入所有模块似乎是每个人都会这么做的操作,事实上我们没有必要导入全部的模块。如果模块太大,则根据需要导入它是一个更好的主意。
def my_function ():
import heavy_module
如上面的代码所示,heavy_module 在函数中导入。这是一种'延迟加载'的思想:只有 my_function 被调用的时候该模块才会被导入。
这种方法的好处是,如果 my_function 在脚本执行期间从未调用过,则 heavy_module 永远不会加载,从而节省资源并减少脚本的启动时间。这对于大型应用或微服务架构尤为重要。
补充:性能分析与优化原则 除了上述具体的编码技巧外,理解性能优化的基本原则同样重要。
1. 先测量,再优化 不要盲目猜测哪里慢。使用 Python 内置的 cProfile 或第三方工具如 line_profiler 来分析代码的实际耗时。很多时候,瓶颈并不在字符串拼接或循环上,而在网络 I/O 或数据库查询上。
python -m cProfile your_script.py
2. 算法优于微优化 有时候,改变算法带来的性能提升远大于任何语言层面的微优化。例如,将 O(n^2) 的算法改为 O(n log n),其效果远超使用 join 代替 +=。在编写复杂逻辑前,先思考数据结构的选择。
3. 可读性与性能的权衡 Python 社区有一句名言:'可读性是重要的'。除非你确定某个部分构成了性能瓶颈,否则不要为了微小的性能提升牺牲代码的可读性。清晰的代码更容易维护,也能减少潜在的 Bug。
结语 本文介绍了 9 个提升 Python 代码运行效率的实用技巧,涵盖了从字符串处理、数据结构选择到函数缓存等多个方面。希望这些技巧能帮助你在日常开发中写出更高效、更优雅的 Python 代码。记住,性能优化是一个持续的过程,结合合理的工具分析和良好的编程习惯,才能发挥 Python 的最大潜力。
相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
curl 转代码 解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
HTML转Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online