ython优化指南:让代码运行速度提高5倍。
微信搜索关注《Python学研大本营》,加入读者群,分享更多精彩
探索使Python代码运行更快、更智能、更高效的艺术与科学。
Python代码优化
在编程语言的世界中,Python作为最通用的语言之一,具有简单易读的特点。Python因其简洁易读的语法、面向对象的特性、社区支持和丰富的库资源而脱颖而出。Python在数据分析、人工智能、Web开发、游戏开发等领域中广受开发者欢迎。
然而,与其他编程语言一样,Python的优点也伴随着一些代价。其中一个问题就是性能优化。由于它是解释型语言,人们对其速度和性能有所担忧。这就是代码优化发挥作用的地方。
什么是代码优化?
Python是一种解释型语言,这意味着它可能不像C或C++这样的编译型语言运行得那么快。然而,用户可以利用某些技术和策略来优化Python代码,并提高其性能。
代码优化的目标是使代码运行得更快、使用更少的资源、执行得更顺畅,从而提高其性能和效率。
本文将带你深入了解使Python代码运行得更快、更智能、更高效的艺术与科学。你将学习到各种技术、策略和最佳实践,使开发人员在克服Python的某些性能限制的同时,充分发挥Python的潜力。
本文将使用Python的timeit
模块进行基准测试,并比较传统Python代码与优化后Python代码的执行时间。请注意,默认情况下,timeit
模块会运行该函数一百万次。
言归正传,跟随本文直接进入优化技术和策略。
1. 使用推导式和生成器
在2.7版本和3.0版本中,Python引入了诸如列表推导式、字典推导式和紧密相关的集合推导式等新特性。这些特性以更清晰、更简洁和更高效的方式生成列表、字典和集合,使其更易于使用。
创建一个函数并使用传统的“循环和追加”方法生成一个列表:
>>> def do_1():
... list_object = []
... for i in range(100):
... list_object.append(i)
导入Python内置的timeit
模块,查看该函数的运行时间:
>>> import timeit
>>> t = timeit.Timer(setup='from __main__ import do_1', stmt='do_1()')
>>> print(t.timeit())
9.681053700041957
上面的输出显示,该函数运行时间约为9.68秒。
现在,使用推导式生成这个列表,并查看所需的时间:
>>> def do():
... [i for i in range(100)]
>>> t = timeit.Timer(setup='from __main__ import do', stmt='do()')
>>> t.timeit()
7.292758799972944
从以上代码中可以看出,这个函数运行时间约为7.29秒,而之前的函数(没有使用推导式)运行时间为9.68秒,差距为 2.39秒。
除了推导式更简洁、更易读外,它的速度也更快。这使得它成为生成列表和一般循环的首选方法。
2. 避免字符串拼接
开发者通常使用+=
运算符来对字符串进行拼接。然而,在循环内部,由于字符串的不可变性,这种方法可能导致性能下降。
相反,可以使用str.join()
方法进行高效的字符串拼接。
使用+=
运算符拼接字符串并查看其执行时间:
>>> def do():
... obj = ["hello", "my", "name", "is", "Delight", "!"]
... s = ""
... for elem in obj:
... s += elem
>>> import timeit
>>> t = timeit.Timer(setup='from __main__ import do', stmt='do()')
>>> t.timeit()
0.9870554000372067
这个使用+=
运算符实现字符串拼接的函数大约需要0.98秒的时间来完成。
使用join()
进行有效的字符串拼接:
>>> def do():
... s = ["hello", "my", "name", "is", "Delight", "!"]
... "".join(s)
>>> import timeit
>>> t = timeit.Timer(setup='from __main__ import do', stmt='do()')
>>> t.timeit()
0.38973289995919913
使用join()
可以将函数的执行时间从0.98秒降低到0.38秒,使其成为更快、更可取的字符串拼接方法。
3. 循环
在Python中,for
循环是一种控制结构,它允许用户迭代遍历一个序列中的项,例如列表中的元素、字符串中的字符或其他可迭代对象中的元素。for
循环会针对序列中的每个项重复执行一段代码块,从第一项迭代到最后一项。
然而,在大多数情况下,for
循环可以被更高效的函数map()
取代。map()
函数是一个内置的高阶函数,它允许用户将给定的函数应用于可迭代对象(如列表、元组或字符串)中的每个项,并生成一个包含将该函数应用于每个项的结果的新可迭代对象。使用map()
的主要优势在于它提供了一种简洁高效的方式来转换数据,而无需使用显式循环。
比较实现for
循环和map()
的函数的执行时间:
def do():
obj = ["hello", "my", "name", "is", "Delight", "!"]
new = []
for i in obj:
new.append(i.upper())
获取该函数的执行时间:
>>> import timeit
>>> t = timeit.Timer(setup='from __main__ import do', stmt='do()')
>>> t.timeit()
1.042804399970919
使用内置的map()
函数执行相同的函数,并获取执行时间:
>>> def square(x):
... return x.upper()
>>> def do():
... obj = ["hello", "my", "name", "is", "Delight", "!"]
... map(square, obj)
>>> import timeit
>>> t = timeit.Timer(setup='from __main__ import do', stmt='do()')
>>> t.timeit()
0.37273399997502565
在上面的代码中,使用map()
代替for
循环使函数的运行速度提高了约3倍。
Python的内置函数之所以能使代码运行得更快,主要是因为它们是用C语言编写和编译的。
4. 选择合适的数据结构
选择合适的数据结构可以显著影响Python代码的速度和效率。不同的数据结构针对特定类型的操作进行了优化,选择适当的数据结构可以实现更快的查找、插入、删除和整体性能改进。
例如,使用集合进行成员测试要比使用列表更快:
>>> def do():
... fruits_list = ['apple', 'banana', 'orange', 'grape', 'pear']
... 'banana' in fruits_list
... 'kiwi' in fruits_list
>>> import timeit
>>> t = timeit.Timer(setup='from __main__ import do', stmt='do()')
>>> t.timeit()
0.48580530006438494
>>> def do():
... fruits_list = {'apple', 'banana', 'orange', 'grape', 'pear'}
... 'banana' in fruits_list
... 'kiwi' in fruits_list
>>> import timeit
>>> t = timeit.Timer(setup='from __main__ import do', stmt='do()')
>>> t.timeit()
0.34570479998365045
5. 避免使用全局变量
全局变量在整个程序中共享数据方面发挥着重要的作用。然而,应谨慎使用全局变量,仅在必要时使用。访问全局变量比访问局部变量要慢。请注意始终尽量减少对全局变量的使用,特别是在循环内部。
6. 向量化
在Python中,向量化指的是将操作应用于整个数组或数据序列的实践,而不是使用显式循环来迭代单个元素。它利用像NumPy这样的专门库,在数组上高效地执行逐元素的操作,利用硬件级别的优化,减少了对显式循环结构的需求。
如果正在进行数值计算,可以考虑使用像NumPy这样的库,它提供了优化的数组操作,速度明显快于在标准Python列表中执行按元素排列的操作。
向量化是科学计算和数值分析中的基本概念,它在使Python成为数据分析、机器学习和涉及大型数据集的其他计算任务的强大语言方面发挥着至关重要的作用。
7. 避免不必要的函数调用
在Python中避免不必要的函数调用对于提高代码的效率和性能非常重要。不必要的函数调用可能引入额外开销,消耗内存,并降低程序的执行速度。尽可能合并操作。
8. 避免不必要的导入语句
导入语句几乎可以在任何地方执行。将它们放在函数内部以限制其可见性和/或减少初始启动时间通常是很有用的。——Python.org
在Python中避免不必要的导入语句对于保持代码的简洁、高效和可读性是至关重要的。不必要的导入有时可能导致模块之间的循环依赖。这可能导致运行时产生问题,并使重构代码变得更加困难。
尽管现在的文本编辑器可以轻松帮助你识别程序中未使用的代码,但添加未使用的导入可能会使这些编辑器感到困惑。
9. 编译代码
如果性能是程序执行的关键因素,那么可以考虑使用像Cython这样的工具将代码编译为C或C++。这样可以显著提高程序的性能。
通过使用像Cython这样的工具,可以充分利用Python的表达能力和C或C++等编译语言的性能。
10. 对代码进行性能分析
加快程序的第一步是了解瓶颈所在。——Python.org
在Python中进行性能分析是测量和分析代码的执行时间及其资源使用情况,以便查找代码中的瓶颈和优化机会。
Python提供了各种内置的性能分析工具,如profile
和timeit
(本文在示例中使用了它们)。
以下是使用profile
模块对名为do()
的函数进行性能分析的示例:
import profile
profile.run('do()')
保持Python版本的最新更新
更新的Python版本通常包含性能增强、错误修复、安全更新等。
Python在不断发展,新版本通常包含性能改进。及时更新解释器可以帮助用户从这些改进中受益。
然而,保持最新与考虑升级对现有项目的影响之间的平衡是很重要的。如果有一个庞大的代码库或是依赖于尚不兼容最新版本的第三方库,那么可能需要谨慎地升级。
结论
在本文中介绍了各种技术、策略和最佳实践,使开发人员可以充分发挥Python的潜力,同时克服其在性能方面的某些限制。
需要注意的是,优化不仅仅是从代码的执行时间中节省毫秒或微秒的问题;它也涉及要编写更具可读性、可扩展性和可维护性的代码。
推荐书单
IT BOOK 多得(点击查看5折活动书单)
https://u.jd.com/psx2y1M
《Python从入门到精通(第3版)》
《Python从入门到精通(第3版)》从初学者角度出发,通过通俗易懂的语言、丰富多彩的实例,详细介绍了使用Python进行程序开发应该掌握的各方面技术。全书共分27章,包括初识Python、Python语言基础、运算符与表达式、流程控制语句、列表和元组、字典和集合、字符串、Python中使用正则表达式、函数、面向对象程序设计、模块、文件及目录操作、操作数据库、使用进程和线程、网络编程、异常处理及程序调试、Pygame游戏编程、推箱子游戏、网络爬虫开发、火车票分析助手、数据可视化、京东电商销售数据分析与预测、Web编程、Flask框架、e起去旅行网站、Python自动化办公、AI图像识别工具等内容。书中所有知识都结合具体实例进行介绍,涉及的程序代码都给出了详细的注释,读者可轻松领会Python程序开发的精髓,快速提升开发技能。
《Python从入门到精通(第3版)》https://item.jd.com/14055900.html
精彩回顾
微信搜索关注《Python学研大本营》,加入读者群
访问【IT今日热榜】,发现每日技术热点