Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/348.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 为什么[None]*10比[None for i在范围(10)]_Python_Performance_List Comprehension_Timeit - Fatal编程技术网

Python 为什么[None]*10比[None for i在范围(10)]

Python 为什么[None]*10比[None for i在范围(10)],python,performance,list-comprehension,timeit,Python,Performance,List Comprehension,Timeit,我想创建一个带有一些初始化值的列表,因为空列表在python中不是一个选项。所以我开始思考哪一个会更快: l=[范围(1000)内的i无] 或 l=[None]*1000 我试着用timeit测试它: In [56]: timeit.timeit('l = [None] * 1000', number=10000) Out[56]: 0.04936316597741097 In [58]: timeit.timeit('l = [None for i in range(1000)]', numb

我想创建一个带有一些初始化值的列表,因为空列表在python中不是一个选项。所以我开始思考哪一个会更快:
l=[范围(1000)内的i无]
l=[None]*1000
我试着用
timeit
测试它:

In [56]: timeit.timeit('l = [None] * 1000', number=10000)
Out[56]: 0.04936316597741097
In [58]: timeit.timeit('l = [None for i in range(1000)]', number=10000)
Out[58]: 0.2318978540133685
我很惊讶
[None]*1000
更快

  • 为什么会这样(我的性能测试方法正确吗)
  • 有没有更快的方法初始化“空”列表

  • 我想你用的是CPython。让我们将生成的Python bytcode与。这是第一个版本:

    >>> import dis
    >>> def f():
    ...     return [None] * 1000
    >>> dis.dis(f)
      2           0 LOAD_CONST               0 (None)
                  2 BUILD_LIST               1
                  4 LOAD_CONST               1 (1000)
                  6 BINARY_MULTIPLY
                  8 RETURN_VALUE
    
    这一点非常清楚:创建一个列表
    [None]
    (第0-2行),然后乘以
    1000
    (第4-6行)

    这是第二个版本:

    >>> def g():
    ...     return [None for _ in range(1000)]
    >>> dis.dis(g)
      2           0 LOAD_CONST               1 (<code object <listcomp> at ..., file "<doctest __main__[3]>", line 2>)
                  2 LOAD_CONST               2 ('g.<locals>.<listcomp>')
                  4 MAKE_FUNCTION            0
                  6 LOAD_GLOBAL              0 (range)
                  8 LOAD_CONST               3 (1000)
                 10 CALL_FUNCTION            1
                 12 GET_ITER
                 14 CALL_FUNCTION            1
                 16 RETURN_VALUE
    
    创建一个空列表(第0行),迭代器参数(
    iter(range(1000))
    )被推送到堆栈上(第2行),for循环开始(第4行)。循环索引(
    )的值存储在本地数组(第6行)中,
    None
    被追加到列表(第8-10行),直到循环结束(第12行循环到第4行)

    总结如下:

    • 第一个版本:乘法
    • 第二个版本:创建一个局部函数,创建一个范围,并将迭代器传递给该函数;此函数在迭代器上迭代并逐个追加元素
    第二个版本确实较慢


    注意:小心常见的陷阱

    >>> A = [[0]] * 3
    >>> A
    [[0], [0], [0]]
    >>> A[0].append(1)
    >>> A
    [[0, 1], [0, 1], [0, 1]]
    
    但是:


    如果你想知道为什么,看看上面的字节码。

    l=[]
    是一个“空”列表,不需要进入(CPython)实现(你可以反汇编它),对我来说,简单的乘法比创建一个range对象并在其上循环要快得多,列表理解本质上是循环的解释器级别(有一些优化)。它还使用了
    .append
    ,因此每隔一段时间,整个底层数组都会调整大小。如果使用
    *
    操作符查看列表重复的源代码,它会预先分配整个底层数组,而不必调整大小,循环是在C级完成的…这里有一个相关的问题可能是illuminating@zvone:如果不是按照从前到后的顺序填写列表,则算法通常需要一个伪值列表。
    >>> A = [[0]] * 3
    >>> A
    [[0], [0], [0]]
    >>> A[0].append(1)
    >>> A
    [[0, 1], [0, 1], [0, 1]]
    
    >>> A = [[0] for _ in range(3)]
    >>> A
    [[0], [0], [0]]
    >>> A[0].append(1)
    >>> A
    [[0, 1], [0], [0]]