在Python中,将列表初始化为已知数量的元素

284

目前我正在使用一个列表,希望得到类似下面的内容:

verts = list (1000)

我应该使用数组吗?


为什么?你必须在随机位置设置元素吗? - Torsten Marek
10
为什么?我有一系列物品需要按顺序排列。你们知道如何做吗?史蒂夫的回复似乎是唯一的方法。 - Joan Venge
42
我对评论的数量感到惊讶(并为@JoanVenge感到有点难过),这些评论偏离了主题。在我看来,标准答案首先应该包括如何完成任务(无论多么荒谬),然后再对问题进行警告/建议。否则就显得毫无意义。质疑问题的有效性也可能会被质疑。 - Shashank Sawant
15
欢迎来到SO。 - Joan Venge
我有一个使用案例,需要通过2次填充列表。第一遍填入特定已知索引的值。第二遍填充其余部分,并跳过在前一次填充中已经被填充的项目。我认为,除非初始化列表,否则无法在特定索引处填充值。 - Raj
显示剩余2条评论
9个回答

398

我首先想到的是:

verts = [None]*1000

但是你真的需要预先初始化它吗?


22
没错,这正是重点。“过早优化是万恶之源”的意思是,在一开始编写代码时不要在意性能问题。如果后来发现代码运行缓慢,那么就回头来进行像这种优化。 - David Z
32
不,过早优化是指你试图优化那些你不确定需要进行优化的代码。你不应该总是编写最快的代码——其他因素,如商业目标、维护成本、编写工程时间,通常更为重要。 - user26294
105
请注意,除了优化的情况外,存在其他合法的情况需要预先分配数组。可能使用它的代码不会添加元素,只是替换现有元素,因此更像一个数组而不是一个列表。 - Lasse V. Karlsen
61
这种初始化Python数组的方式很危险:a=[[]]*2; a[0].append('foo'); 现在检查 a[1],你会感到震惊。相比之下,a=[[] for k in range(2)] 就可以正常工作。 - Joachim W
17
请检查您的假设。例如,我目前正在通过解析日志文件并将错误放入一个数组中的bin中来分析网络错误率,当前为每小时4个bins和24小时/天。一天的小时数不会改变,如果我更改每小时的数量,我将停止并重新启动程序,因此我始终希望(当前)4 * 24 = 96 bins。对于我(具有C / C ++ / C#/等背景),将每个bin初始化为0似乎是自然的。这种优化是怎样的,是否为过早优化? - Technophile
显示剩余15条评论

85

不太清楚为什么大家都反对你想做这个 - 有几种情况下,你会想要一个固定大小的初始化列表。而你已经正确地推断出在这些情况下使用数组是合理的。

import array
verts=array.array('i',(0,)*1000)
对于非Python程序员来说,(0,)*1000 表示创建一个包含1000个零的元组。逗号强制Python将 (0) 视为元组,否则它会被解释为数字0。
我使用元组而不是列表,因为它们通常具有更低的开销。

7
有些人可能太过于字面理解“过早”的优化了吧。 - Joan Venge
7
谢谢!这个解决方案完全符合我的期望。在性能分析时,列表初始化是我代码中的瓶颈,而这个方法使其快了2倍。 - Frederik
81
很遗憾,在 Stack Overflow 上,我还没有找到一个不含有那些自以为是的“你为什么要这么做?”之类的宿舍式傲慢态度的回答来解决我的 Python 问题。唉,“社区”啊。 - tomato
2
@mikerodent Joan这个名字在世界上一些国家是男性名字,包括法国、西班牙和荷兰。 - Chris
@MAx 我认为数组是快速的。不管怎样,将“i”作为数组的第一个参数是为了整数。 - Timo
显示剩余2条评论

68

一种明显但可能不太有效的方法是

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

请注意,这可以很容易地扩展到二维。例如,要获取一个10x100的“数组”,可以执行:

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

39

在任何编程语言中,初始化一个固定大小的数组是完全可以接受的;它不像程序员想在while(true) 循环中放置一个break语句。相信我,特别是如果元素只是被覆盖而不是仅仅添加/减去的情况,就像许多动态编程算法一样,你不想在代码中使用append语句和检查元素是否已经被初始化(那是很多代码啊)。

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

这将对程序员试图实现的目标起作用。


1
+1. 我曾担心用预定义大小初始化数组是否正确,你的回答让我感到放心。 - smajli

29

@Steve已经给出了对你的问题很好的答案:

verts = [None] * 1000

警告:正如@Joachim Wuttke指出的那样,列表必须用不可变元素初始化。 [[]] * 1000不能按预期工作,因为您将获得1000个相同的列表(类似于C中指向同一列表的1000个点的列表)。像int、str或tuple这样的不可变对象会很好地解决这个问题。

替代方案

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

>>> 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)和固定长度初始化列表以避免列表调整大小。例如:

>>> %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或其他优化库更快:

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

4
你可以这样做:
verts = list(xrange(1000))

这将给你一个由1000个元素组成的列表,它被初始化为0-999的值。由于list首先执行__len__以确定新列表的大小,因此效率应该相当高。


5
在Python 3.0之前,范围可以写成range(1000);在Python 3.0中,需要写成list(range(1000))。 - user3850

0
你应该考虑使用字典类型而不是预初始化列表。字典查找的成本很小,与访问任意列表元素的成本相当。
而且在使用映射时,你可以这样写:
aDict = {}
aDict[100] = fetchElement()
putElement(fetchElement(), fetchPosition(), aDict)

putElement函数可以在任何给定位置存储项目。如果您需要检查集合是否包含给定索引处的元素,则更符合Python风格的写法是:

if anIndex in aDict:
    print "cool!"

比:

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

由于后者假定您的集合中没有None元素。如果发生这种情况,您的代码将无法正常工作。

如果您迫切需要性能,并因此尝试预初始化变量并编写最快的代码,则应更改语言。最快的代码无法在Python中编写。您应该尝试使用C语言,并实现包装器以从Python调用预初始化和预编译代码。


-2

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

verts = []

你真的看到了性能问题吗?如果是这样,性能瓶颈是什么? 不要试图解决你没有的问题。动态填充一个数组到1000个元素的性能成本可能对你真正想编写的程序来说是完全无关紧要的

如果列表中的内容始终是特定的原始固定长度类型(例如char、int、float),那么数组类很有用。但是,它也不需要预先初始化。


7
你没有看到重点。我只是想创建一个预定义元素数量的列表/数组。对于为什么和如何需要进行评论是愚蠢的。我知道自己在做什么。谢谢。 - Joan Venge
4
当我说“我知道自己在做什么”时,我是指在编程方面,而不是Python语言方面。如果我懂Python的话,我就不会问这个问题了,对吧? - Joan Venge
2
你能编辑一下问题并解释一下背景吗?从这个问题中,不清楚正确的答案是什么,也不清楚你知道自己在做什么。 - user26294

-4

这个:

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

创建一个列表,元素初始化为8。
但是这个:
lst = [0] * 7

会创建7个只有一个元素的列表


10
[0] * 7 的结果是 [0, 0, 0, 0, 0, 0, 0],这是一个包含7个元素的列表。你是在描述某个很旧的 Python 版本的行为吗? - FooF
他说的是这个列表包含7个元素,但这7个元素都指向同一块内存。对其中任何一个元素的修改都会相应地改变其他元素。 - York
2
嗯,如果元素是整数,那么不会有问题吧?我刚试了mylist = [0] * 4,然后执行mylist[0] = 12mylist返回[12, 0, 0, 0] - toonarmycaptain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接