我试图理解Python中列表推导的性能,以及使用它们与循环创建列表的权衡。使用for循环将元素附加到列表的已知性能成本之一是在每次迭代时它是O(k)(其中k是列表的长度),因为append需要到达列表的末尾添加一个额外的元素。
这对列表推导有什么用?在每次迭代时,是否需要到达新列表的末尾以附加新元素?
elementtree.SimpleXMLTreeBuilder.TreeBuilder
我搜索过Python文档,Stack Overflow和其他网站,但无法找到有关此内容的任何信息。关于列表推导的更多更高级别的信息有很多资源,但没有类似的具体内容。
如果你无法提供答案,请指导我,或者告诉我如何查看实际的基础Python列表理解代码,以便我可以自己做这个?
答案 0 :(得分:5)
附加到列表已摊销 O(1)
而不是O(k)
;列表实现为可变长度数组,而不是链接列表。复杂性适用于for
循环和my_list.append
调用以及列表推导(其中,扰码警报,也追加)。
所以在这两种情况下。复杂性为O(N)
。
列表推导通常表现更好,因为他们专门做一件事: 创建列表。为它们生成的字节码是特定的。 (参见LIST_APPEND
字节码)
另请注意,列表推导(如for循环)不必在每次迭代时附加。使用if
子句来过滤掉循环的元素,这是常用的。
如果您想了解如何在CPython中实现list-comprehensions,您可以查看为它们生成的字节码,并浏览ceval.c
以查找为每个执行的操作。
编译列表推导表达式后,可以用dis
看到字节码:
dis(compile('[i for i in range(10)]', '', 'exec').co_consts[0])
1 0 BUILD_LIST 0
2 LOAD_FAST 0 (.0)
>> 4 FOR_ITER 8 (to 14)
6 STORE_FAST 1 (i)
8 LOAD_FAST 1 (i)
10 LIST_APPEND 2
12 JUMP_ABSOLUTE 4
>> 14 RETURN_VALUE
然后,scan through the cases in ceval.c
或查看dis
module中的文档。