Python:是否在列表理解中对split函数进行了多次评估?


问题内容

我一直想知道有一段时间。在此列表理解中拆分是执行一次还是多次?

l = [line.split()[i] for i in indexes]

我目前确实以这种方式列出了这样的理解:

l = line.rstrip().split()
l = [l for i in indexes]

但是我不确定是否有必要。除了肯定/否定答案外,我肯定想知道,如何通过执行CPU性能分析或阅读一些文档来自己找到有关此问题的信息。谢谢。


问题答案:

对于每个元素,列表推导中左侧的表达式将重新评估,是的。

如果只需要对它进行一次评估,则需要完全按照自己的意愿进行;首先调用它,并将结果存储在列表推导中。

列表显示 文档

在这种情况下,新列表的元素是通过将每个fororif子句视为一个块,从左到右嵌套,
并在每次到达最里面的块时评估表达式以产生一个列表元素而生成的元素

强调我的。

您还可以使用dis.dis()函数反汇编列表理解:

>>> import dis
>>> dis.dis(compile('[line.split()[i] for i in indexes]', '', 'eval'))
  1           0 BUILD_LIST               0
              3 LOAD_NAME                0 (indexes)
              6 GET_ITER            
        >>    7 FOR_ITER                22 (to 32)
             10 STORE_NAME               1 (i)
             13 LOAD_NAME                2 (line)
             16 LOAD_ATTR                3 (split)
             19 CALL_FUNCTION            0
             22 LOAD_NAME                1 (i)
             25 BINARY_SUBSCR       
             26 LIST_APPEND              2
             29 JUMP_ABSOLUTE            7
        >>   32 RETURN_VALUE

FOR_ITER操作码开始循环(与JUMP_ABSOLUTE关闭它),每一次一个LOAD_NAME lineLOAD_ATTR splitCALL_FUNCTION被执行。换句话说,字节码13到19实现了该line.split()部分,并且每次通过循环执行,该循环从字节码7到29运行。

(Python
3注意:列表推导具有其自身的作用域,您需要从外部代码对象常量中提取代码对象;dis.dis(compile('[line.split()[i] for i in indexes]', '', 'eval').co_consts[0]))。