9个使Python代码更快的微妙技巧。
微信搜索关注《Python学研大本营》,加入读者群,分享更多精彩
图片来自Wallhaven
“Python太慢了。”
这种观点在关于编程语言的讨论中经常出现,经常掩盖了Python的众多优点。
事实是,如果能以Pythonic的方式编写Python代码,它是很快的。
细节决定成败。经验丰富的Python开发者掌握了一系列微妙而强大的技巧,可以显著提高代码的性能。
这些技巧乍看之下似乎微不足道,但它们可以带来效率的大幅提升。让我们深入了解其中的9种方法,改变编写和优化Python代码的方式。
join()
”或“+
”如果有大量字符串等待处理,字符串连接将成为Python程序的瓶颈。
基本上,Python有两种字符串连接的方式:
使用join()
函数将一组字符串合并为一个字符串。
使用+
或+=
符号将每个单独的字符串添加到一个字符串中。
那么哪种方式更快呢?
现在,让我们定义3个不同的函数来连接相同的字符串:
mylist = ["Yang", "Zhou", "is", "writing"]
# 使用'+'
def concat_plus():
result = ""
for word in mylist:
result += word + " "
return result
# 使用'join()'
def concat_join():
return " ".join(mylist)
# 直接连接而不使用列表
def concat_directly():
return "Yang" + "Zhou" + "is" + "writing"
根据你的第一印象,你认为哪个函数最快,哪个最慢?
真正的结果可能会让你惊讶:
import timeit
print(timeit.timeit(concat_plus, number=10000))
# 0.002738415962085128
print(timeit.timeit(concat_join, number=10000))
# 0.0008482920238748193
print(timeit.timeit(concat_directly, number=10000))
# 0.00021425005979835987
如上所示,对于连接一组字符串,join()
方法比在for
循环中逐个添加字符串更快。
原因很简单。一方面,字符串在Python中是不可变的数据,每次+=
操作都会创建一个新字符串并复制旧字符串,这在计算上成本是昂贵的。
另一方面,.join()
方法专门针对连接一系列字符串进行了优化。它会预先计算出所生成字符串的大小,然后一次性创建它。因此,它避免了循环中+=
操作带来的开销,从而使速度更快。
然而,在我们的测试中,速度最快的函数是直接连接字符串文字。它的高速度是由于:
Python解释器可以在编译时优化字符串文字的连接,将它们转换为一个单独的字符串文字。这里不涉及循环迭代或函数调用,因此是一种非常高效的操作。
由于所有字符串在编译时都是已知的,Python可以非常快速地执行此操作,比在循环中进行的运行时连接或经过优化的.join()
方法要快得多。
总之,如果需要连接一组字符串,请选择join()
而不是+=
。如果想要直接连接字符串,只需使用+
即可。
[]
”而不是“list()
”创建列表并不是很难的事情。常见的两种方式是:
使用list()
函数。
直接使用[]
。
让我们使用一个简单的代码片段来测试它们的性能:
import timeit
print(timeit.timeit('[]', number=10 ** 7))
# 0.1368238340364769
print(timeit.timeit(list, number=10 ** 7))
# 0.2958830420393497
结果显示,执行list()
函数比直接使用[]
要慢。
这是因为[]
是一种字面量语法,而list()
是一个构造函数调用。调用函数无疑需要额外的时间。
从同样的逻辑出发,在创建字典时,我们也应该使用{}
而不是dict()
。
成员测试操作的性能在很大程度上依赖于底层数据结构:
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))
# 0.01112208398990333
print(timeit.timeit(set_membership_test, number=1000))
# 3.27499583363533e-05
正如上述代码所示,使用集合进行成员测试比使用列表更快。
为什么会这样呢?
在Python的列表中,成员测试(列表中的元素)是通过迭代每个元素直到找到所需的元素或达到列表的末尾来完成。因此,此操作的时间复杂度为O(n)。
Python中的集合实现形式为哈希表。在进行成员检查(集合中的元素)时,Python使用哈希机制,其平均时间复杂度为O(1)。
这里的关键在于:在编写程序时要仔细考虑底层数据结构。正确利用合适的数据结构可以显著加快代码的运行速度。
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))
# 0.2797503340989351
print(timeit.timeit(generate_squares_comprehension, number=10000))
# 0.2364629579242319
上述代码是列表推导式和for
循环之间的简单速度比较。结果显示,列表推导式更快。
《Python从入门到精通(第3版)》从初学者角度出发,通过通俗易懂的语言、丰富多彩的实例,详细介绍了使用Python进行程序开发应该掌握的各方面技术。全书共分27章,包括初识Python、Python语言基础、运算符与表达式、流程控制语句、列表和元组、字典和集合、字符串、Python中使用正则表达式、函数、面向对象程序设计、模块、文件及目录操作、操作数据库、使用进程和线程、网络编程、异常处理及程序调试、Pygame游戏编程、推箱子游戏、网络爬虫开发、火车票分析助手、数据可视化、京东电商销售数据分析与预测、Web编程、Flask框架、e起去旅行网站、Python自动化办公、AI图像识别工具等内容。书中所有知识都结合具体实例进行介绍,涉及的程序代码都给出了详细的注释,读者可轻松领会Python程序开发的精髓,快速提升开发技能。
《Python从入门到精通(第3版)》https://item.jd.com/14055900.html
精彩回顾
《数据科学必备的Python前端库Top-5》
《轻松上手,本地运行LlaMA 2的简易指南》
《8个Python开发者必备的PyCharm插件》
《数据科学不可或缺的10个Python库,让你事半功倍》
《掌握Python设计模式,SQL Alchemy助你打破ORM与模型类的束缚》
《Python进阶之路,2024年7个不可错过的技巧》
微信搜索关注《Python学研大本营》,加入读者群,分享更多精彩
访问【IT今日热榜】,发现每日技术热点