Python 3:在范围(N)列表理解中创建[func(i)for i的最有效方法

mej*_*iwa 7 python list-comprehension

假设我有一个函数func(i)为一个整数i创建一个对象,N是一个非负整数.那么创建等于此列表的列表(不是范围)的最快方法是什么

mylist = [func(i) for i in range(N)]
Run Code Online (Sandbox Code Playgroud)

不采用像在C中创建函数的高级方法?我对上面列表理解的主要关注是,我不确定python是否事先知道预分配mylist的范围(N)的长度,因此必须逐步重新分配列表.是这样的情况还是python足够聪明,首先将mylist分配给长度N然后计算它的元素?如果没有,创建mylist的最佳方法是什么?也许这个?

mylist = [None]*N
for i in range(N): mylist[i] = func(i)
Run Code Online (Sandbox Code Playgroud)

重新编辑:从以前的编辑中删除了误导性信息.

Joh*_*hin 7

有人写道:"""Python足够聪明.只要您迭代的对象有一个__len__或一个__length_hint__方法,Python就会调用它来确定大小并预先分配数组."""

据我所知,列表理解中没有预分配.Python无法从INPUT的大小告知OUTPUT的大小.

看看这个Python 2.6代码:

>>> def foo(func, iterable):
...     return [func(i) for i in iterable]
...
>>> import dis; dis.dis(foo)
  2           0 BUILD_LIST               0 #### build empty list
              3 DUP_TOP
              4 STORE_FAST               2 (_[1])
              7 LOAD_FAST                1 (iterable)
             10 GET_ITER
        >>   11 FOR_ITER                19 (to 33)
             14 STORE_FAST               3 (i)
             17 LOAD_FAST                2 (_[1])
             20 LOAD_FAST                0 (func)
             23 LOAD_FAST                3 (i)
             26 CALL_FUNCTION            1
             29 LIST_APPEND      #### stack[-2].append(stack[-1]); pop()
             30 JUMP_ABSOLUTE           11
        >>   33 DELETE_FAST              2 (_[1])
             36 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)

它只是构建一个空列表,并附加迭代提供的任何内容.

现在看看这个代码,它在列表理解中有一个'if':

>>> def bar(func, iterable):
...     return [func(i) for i in iterable if i]
...
>>> import dis; dis.dis(bar)
  2           0 BUILD_LIST               0
              3 DUP_TOP
              4 STORE_FAST               2 (_[1])
              7 LOAD_FAST                1 (iterable)
             10 GET_ITER
        >>   11 FOR_ITER                30 (to 44)
             14 STORE_FAST               3 (i)
             17 LOAD_FAST                3 (i)
             20 JUMP_IF_FALSE           17 (to 40)
             23 POP_TOP
             24 LOAD_FAST                2 (_[1])
             27 LOAD_FAST                0 (func)
             30 LOAD_FAST                3 (i)
             33 CALL_FUNCTION            1
             36 LIST_APPEND
             37 JUMP_ABSOLUTE           11
        >>   40 POP_TOP
             41 JUMP_ABSOLUTE           11
        >>   44 DELETE_FAST              2 (_[1])
             47 RETURN_VALUE
>>>
Run Code Online (Sandbox Code Playgroud)

相同的代码,加上一些代码来避免LIST_APPEND.

在Python 3.X中,你需要深入挖掘一下:

>>> import dis
>>> def comprehension(f, iterable): return [f(i) for i in iterable]
...
>>> dis.dis(comprehension)
  1           0 LOAD_CLOSURE             0 (f)
              3 BUILD_TUPLE              1
              6 LOAD_CONST               1 (<code object <listcomp> at 0x00C4B8D
8, file "<stdin>", line 1>)
              9 MAKE_CLOSURE             0
             12 LOAD_FAST                1 (iterable)
             15 GET_ITER
             16 CALL_FUNCTION            1
             19 RETURN_VALUE
>>> dis.dis(comprehension.__code__.co_consts[1])
  1           0 BUILD_LIST               0
              3 LOAD_FAST                0 (.0)
        >>    6 FOR_ITER                18 (to 27)
              9 STORE_FAST               1 (i)
             12 LOAD_DEREF               0 (f)
             15 LOAD_FAST                1 (i)
             18 CALL_FUNCTION            1
             21 LIST_APPEND              2
             24 JUMP_ABSOLUTE            6
        >>   27 RETURN_VALUE
>>>
Run Code Online (Sandbox Code Playgroud)

它是相同的旧schtick:从构建一个空列表开始,然后迭代迭代,根据需要附加到列表.我在这里看不到预先分配.

您正在考虑的优化在单个操作码中使用,例如,list.extend(iterable)如果iterable可以准确报告其长度,则可以预分配.list.append(object)给出一个对象,而不是可迭代的对象.