简体   繁体   English

Python列表推导是否在每次迭代时附加?

[英]Do Python list comprehensions append at each iteration?

I'm trying to understand the performance of list comprehensions in Python, and the trade-offs of using them versus for loops to create lists. 我试图了解Python中列表理解的性能,以及使用它们与for循环创建列表的权衡。 One of the known performance costs of using a for loop to append elements to a list is that at each iteration it is O(k) (where k is the length of the list) because the append needs to get to the end of the list to add an additional element. 使用for循环将元素追加到列表的已知性能代价之一是,在每次迭代中,它都是O(k)(其中k是列表的长度),因为追加需要到达列表的末尾添加其他元素。

How does this work for list comprehensions? 列表推导如何工作? At each iteration does it need to get to the end of the new list to append a new element? 在每次迭代中,是否都需要到达新列表的末尾以添加新元素?

# For loop:
# O(n*k) (k=number of elements currently in list) time complexity:
new_list = []
for i in range(n): # O(n)
  new_list.append(i) # O(k) 

# List comprehension:
new_list = [i for i in range(n)] # Is this O(n)? 

I've searched the Python documentation, Stack Overflow, and other websites and couldn't find any information about this. 我已经搜索了Python文档,Stack Overflow和其他网站,但找不到有关此的任何信息。 There are many resources for more higher level information about list comprehensions but nothing specific like this. 有很多资源可用于获取有关列表理解的更高级信息,但没有类似的具体信息。

If you can't provide an answer can you please direct me to, or show me how to look at the actual underlying Python list comprehension code so I can do this on my own? 如果您无法提供答案,可以请您指导我,或向我展示如何查看实际的基础Python列表理解代码,以便我自己完成此工作?

Appending to a list is amortized O(1) and not O(k) ; 追加到列表是摊销 O(1)而不是O(k) lists are implemented as variable-length arrays, not as linked-lists. 列表被实现为可变长度数组,而不是链接列表。 The complexity applies both for for loops with my_list.append calls and list-comprehensions (which, spoiler alert, also append). 复杂性既适用for使用my_list.append调用的for循环,也适用于列表理解(后者,扰流器警报, 还可以追加)。

So in both cases. 因此,在两种情况下。 The complexity is O(N) . 复杂度为O(N)

List-comprehensions generally perform better because they are specialized to do one thing: create lists . 列表综合通常表现更好,因为它们专门做一件事: 创建列表 The byte-code produced for them is specific to that. 为它们生成的字节码是特定于此的。 (See the LIST_APPEND bytecode) (请参阅LIST_APPEND字节码)

Also note that list-comprehensions, like for-loops, don't necessarily append at each iteration. 还要注意,列表理解,例如for循环,不一定在每次迭代时都附加。 Using if clauses to filter out elements of the iterable you're looping through is commonly used. 通常使用if子句过滤掉要遍历的可迭代元素。


If you'd like to see how list-comprehensions are implemented in CPython, you can take a look at the bytecode produced for them and scan through ceval.c for the actions performed for each. 如果您想了解如何在CPython中实现列表理解,可以查看为它们生成的字节码,并在ceval.c扫描为每个列表执行的操作。

The byte-code can be seen with dis after we compile a list-comprehension expression: 编译列表理解表达式后,可以在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

Then, scan through the cases in ceval.c or look at their documentation in the dis module . 然后, 浏览ceval.c的案例,或在dis模块中查看其文档。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM