关于数组:在Python中将列表初始化为已知数量的元素

Initializing a list to a known number of elements in Python

本问题已经有最佳答案,请猛点这里访问。

现在,我正在使用一个列表,并期望如下:

1
verts = list (1000)

我应该改用数组吗?


我首先想到的是:

1
verts = [None]*1000

但您真的需要预初始化它吗?


不太清楚为什么每个人都让你很难做到这一点——有几种情况下你需要一个固定大小的初始化列表。您已经正确地推断出数组在这些情况下是合理的。

1
2
import array
verts=array.array('i',(0,)*1000)

对于非pythonistas,(0,)*1000术语正在创建一个包含1000个零的元组。逗号强制python将(0)识别为元组,否则将被评估为0。

我使用了元组而不是列表,因为它们的开销通常较低。


一个明显的而且可能不有效的方法是

1
verts = [0 for x in range(1000)]

注意,这可以很容易地扩展到二维。例如,要获得一个10x100"阵列",您可以这样做

1
verts = [[0 for x in range(100)] for y in range(10)]

在任何编程语言中,想要初始化一个固定大小的数组是完全可以接受的事情;这不像程序员想要在while(true)循环中放入break语句。相信我,尤其是如果元素只是要被覆盖,而不仅仅是加/减,就像许多动态编程算法一样,你不想乱弄APPEND语句,检查元素是否还没有被动态初始化(这是很多代码代理)。

object = [0 for x in range(1000)]

这将适用于程序员试图实现的目标。


@史蒂夫已经给了你一个很好的答案:

1
verts = [None] * 1000

警告:正如@joachim wutke指出的,列表必须用不可变元素初始化。[[]] * 1000无法按预期工作,因为您将获得1000个相同列表的列表(类似于C中相同列表的1000点列表)。像int、str或tuple这样的不可变对象可以做得很好。

选择

调整列表大小很慢。以下结果并不令人惊讶:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> N = 10**6

>>> %timeit a = [None] * N
100 loops, best of 3: 7.41 ms per loop

>>> %timeit a = [None for x in xrange(N)]
10 loops, best of 3: 30 ms per loop

>>> %timeit a = [None for x in range(N)]
10 loops, best of 3: 67.7 ms per loop

>>> a = []
>>> %timeit for x in xrange(N): a.append(None)
10 loops, best of 3: 85.6 ms per loop

但是,如果没有非常大的列表,那么调整大小不是很慢。不要用单个元素(如None和固定长度初始化列表以避免列表大小调整,应考虑使用列表理解并直接用正确的值填充列表。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> %timeit a = [x**2 for x in xrange(N)]
10 loops, best of 3: 109 ms per loop

>>> def fill_list1():
   """Not too bad, but complicated code"""
    a = [None] * N
    for x in xrange(N):
        a[x] = x**2
>>> %timeit fill_list1()
10 loops, best of 3: 126 ms per loop

>>> def fill_list2():
   """This is slow, use only for small lists"""
    a = []
    for x in xrange(N):
        a.append(x**2)
>>> %timeit fill_list2()
10 loops, best of 3: 177 ms per loop

与numpy比较

对于大型数据集,numpy或其他优化的库速度更快:

1
2
3
4
5
6
from numpy import ndarray, zeros
%timeit empty((N,))
1000000 loops, best of 3: 788 ns per loop

%timeit zeros((N,))
100 loops, best of 3: 3.56 ms per loop

你可以这样做:

1
verts = list(xrange(1000))

这将给您一个1000个元素的大小列表,并且恰好用0-999之间的值初始化。正如list首先对新列表进行尺寸调整一样,它应该是相当有效的。


这是:

1
 lst = [8 for i in range(9)]

创建列表,初始化元素8

但这:

1
lst = [0] * 7

将创建7个具有一个元素的列表


您应该考虑使用dict类型,而不是预先初始化的列表。字典查找的成本很小,与访问任意列表元素的成本相当。

当使用映射时,您可以编写:

1
2
3
aDict = {}
aDict[100] = fetchElement()
putElement(fetchElement(), fetchPosition(), aDict)

putElement函数可以在任意给定位置存储项目。如果您需要检查集合中是否包含给定索引处的元素,那么可以编写更多的pythonic:

1
2
if anIndex in aDict:
    print"cool!"

比:

1
2
if not myList[anIndex] is None:
    print"cool!"

因为后者假定集合中没有真正的元素可以是None。如果发生这种情况,你的代码就会出现错误。

如果你非常需要性能,这就是为什么你要尝试预先初始化变量,并尽可能快地编写代码——改变你的语言。最快的代码不能用Python编写。您应该改为尝试c并实现包装器来从python调用预初始化和预编译的代码。


如果不了解问题领域的更多信息,很难回答您的问题。除非您确定需要做更多的事情,否则初始化列表的方法是:

1
verts = []

你真的看到性能问题了吗?如果是,那么性能瓶颈是什么?不要试图解决你没有的问题。动态地将数组填充到1000个元素中的性能成本很可能与您真正想要编写的程序完全无关。

如果列表中的内容总是特定的基元固定长度类型(例如char、int、float),则array类非常有用。但是,它也不需要预初始化。