为什么在Python中快速使用[e] * n(用于创建重复n次的单项列表)?

时间:2014-03-05 20:54:07

标签: python list

跟进: Create List of Single Item Repeated n Times in Python

python -m timeit '[0.5]*100000'
1000 loops, best of 3: 382 usec per loop

python -m timeit '[0.5 for i in range(100000)]'
100 loops, best of 3: 3.07 msec per loop

显然,第二个因为range()而变慢。我不知道为什么[e] * n如此之快(或者它是如何在Python内部实现的)。

2 个答案:

答案 0 :(得分:6)

dis.dis让您查看Python在评估每个表达式时执行的操作:

In [57]: dis.dis(lambda: [0.5]*100000)
  1           0 LOAD_CONST               1 (0.5)
              3 BUILD_LIST               1
              6 LOAD_CONST               2 (100000)
              9 BINARY_MULTIPLY     
             10 RETURN_VALUE        

In [58]: dis.dis(lambda: [0.5 for i in range(100000)])
  1           0 BUILD_LIST               0
              3 LOAD_GLOBAL              0 (range)
              6 LOAD_CONST               1 (100000)
              9 CALL_FUNCTION            1
             12 GET_ITER            
        >>   13 FOR_ITER                12 (to 28)
             16 STORE_FAST               0 (i)
             19 LOAD_CONST               2 (0.5)
             22 LIST_APPEND              2
             25 JUMP_ABSOLUTE           13
        >>   28 RETURN_VALUE        

列表推导执行循环,每次加载常量0.5,并将其附加到结果列表。

表达式[0.5]*100000只需要一个BINARY_MULTIPLY


另请注意,[obj]*N会生成一个长度为N的列表,N引用完全相同的obj

列表推导[expr for i in range(N)] 评估 expr N次 - 即使每次expr评估为相同的值。

答案 1 :(得分:2)

添加到@unutbu所说的内容,BINARY_MULTIPLY最终会在listobject.c执行此紧密循环:

if (Py_SIZE(a) == 1) {
    elem = a->ob_item[0];
    for (i = 0; i < n; i++) {
        items[i] = elem;
        Py_INCREF(elem);
    }
    return (PyObject *) np;
}

这是非常不言自明的:它在紧密的C循环中对同一个对象进行了大量的引用。因此,几乎100%的[obj] * N在本机代码中执行,这意味着它真的快。

关于使用可变对象执行此操作的标准警告适用(即:不要使用可变对象),因为您对同一对象进行了大量引用。