本文作者Chris Conlan,他写了一本书叫做Fast Python,专门分析怎么写python效率(主要是时间效率)最高。但我暂时还没找到电子版,等找到了和大家分享。
这篇文章分析了五种将二维列表平铺为一位列表的方法,并对比他们的时间效率。最后的结果显示,这五种方法的实际运行速度和Python论坛里或者人们的直观感受完全不一样。
原文链接:https://chrisconlan.com/fastest-way-to-flatten-a-list-in-python/
五种方法的代码:
# Use long-form list additiondef slow_add_flatten_lists(the_lists): result = [] for _list in the_lists: result = result + _list return result # Use list addition with the += operatordef add_flatten_lists(the_lists) result = [] for _list in the_lists: result += _list return result # Use List.extenddef extend_flatten_lists(the_lists): result = [] for _list in the_lists: result.extend(_list) return result # Use List.appenddef append_flatten_lists(the_lists) result = [] for _list in the_lists: for val in _list: result.append(val) return result # Use a nested list comprehensiondef comprehension_flatten_lists(the_lists) return [val for _list in the_lists for val in _list]
大多数人会认为第三种或者第五种方法会是最快的,因为它们最简洁而且最好的利用了python中已有的数据结构。但事实并非如此。
实际上,第二种方法,也就是利用 += 操作的方法明显比其他方法要快。
大多数人认为 += 会比较慢,因为 a += b 等价于 a = a + b。而直接把列表用加号加起来应该是比较慢的,所以用 += 应该也比较慢。
但并非如此。
+= 这个操作在列表中被优化过了,完全不等价于直接使用加号。更令人吃惊的是,用了 += 之后,它不仅比正常使用加号的方法快,而且还是五种方法中最快的那个。
由于原作者并没有对运行结果(下面两张图)进行分析,所以薄荷糖在这里简单分析下。
下面两张图分别展示了五种方法的运行速度,和每百万秒五种方法可以处理的数据量。两张图都是log-log图,所以实际运行时间差会比我们看到的更大一些。
我们可以明显看出,在第一张图中,使用 += 操作的方法(蓝色线)明显比其他方法更快一些。直接利用 + 展开(紫色线)确实是最慢的,而且比另外四种方法要慢许多。
作为一个好学宝宝,薄荷糖不能知其然而不知其所以然。原作者只通过实验告诉我们 += 快,但是为什么呢?
这里薄荷糖又去翻了翻源代码,想看看 += 和 + 到底有什么不同。然后发现,+= 其实是in-place operations,也就是在原本的变量之上进行修改。而 + 不是。
举个栗子。
a = a + b
这里 a 和 b 都是列表。那么当两个列表相加的时候,python会开辟另一个存储空间来存放新生成的列表。之后再把新生成的列表赋值给 a。
而使用 += 会直接把 b 加在之前的列表 a 上面,于是不需要额外的存储空间和赋值操作。 因此会快许多。
如果有说错的地方,欢迎指正。