如何在Python中创建一个固定大小的列表?

93

在 C++ 中,我可以像这样创建一个数组...

int* a = new int[10];

在Python中,我只知道可以声明一个列表,然后添加一些项目,或者类似于...

l = [1,2,3,4]
l = range(10)

我能否像C++一样按给定大小初始化列表,而不做任何赋值操作?


3
在Python中,您无需声明列表。只需在您想使用它时进行初始化即可。 - ronakg
4
好的,为什么你需要那个? - Sufian Latif
5
有时候会有这种情况,例如说你想要实现埃拉托斯特尼筛法。 - ninjagecko
4
请注意,在Python3中,range(10)实际上是一个生成器对象;您将无法更改它。您需要使用list(range(10))来进行操作。 - ninjagecko
2
实际上,如果您知道列表的长度,那么首先创建长度为n的“空列表”,然后通过索引分配值比追加每个额外项更快。 - mtnpaul
显示剩余3条评论
9个回答

128

(简而言之,回答你问题的确切方法是numpy.emptynumpy.empty_like,但你可能不在乎,可以使用myList = [None]*10000。)

简单的方法

您可以将列表初始化为相同的元素。是否在语义上使用非数字值(如果您使用它会导致错误,这是一件好事)或类似于0的东西(不寻常?也许在编写稀疏矩阵或“默认”值应该是0的情况下有用,并且您不担心错误)取决于您:

>>> [None for _ in range(10)]
[None, None, None, None, None, None, None, None, None, None]

(这里 _ 只是一个变量名,你可以使用 i。)

你也可以像这样做:

>>> [None]*10
[None, None, None, None, None, None, None, None, None, None]

你可能不需要对此进行优化。每次需要时,也可以将新的元素附加到数组末尾:

>>> x = []
>>> for i in range(10):
>>>    x.append(i)

简单方法性能比较

哪个方法最好?

>>> def initAndWrite_test():
...  x = [None]*10000
...  for i in range(10000):
...   x[i] = i
... 
>>> def initAndWrite2_test():
...  x = [None for _ in range(10000)]
...  for i in range(10000):
...   x[i] = i
... 
>>> def appendWrite_test():
...  x = []
...  for i in range(10000):
...   x.append(i)

在Python 2.7中的结果:

>>> import timeit
>>> for f in [initAndWrite_test, initAndWrite2_test, appendWrite_test]:
...  print('{} takes {} usec/loop'.format(f.__name__, timeit.timeit(f, number=1000)*1000))
... 
initAndWrite_test takes 714.596033096 usec/loop
initAndWrite2_test takes 981.526136398 usec/loop
appendWrite_test takes 908.597946167 usec/loop

Python 3.2 的结果:

initAndWrite_test takes 641.3581371307373 usec/loop
initAndWrite2_test takes 1033.6499214172363 usec/loop
appendWrite_test takes 895.9040641784668 usec/loop

从python2和python3的角度来看,使用 [None]*10000 这个习惯用法可能更好。但是,如果要做的事情比赋值(例如生成或处理列表中的每个元素)更复杂,则开销成为总花费中无意义的一小部分。也就是说,如果你正在对列表元素进行任何合理操作,这样的优化是不必担心的。


未初始化的内存

然而,所有这些方法都很低效,因为它们通过内存,写入一些内容。在C语言中,这是不同的:一个未初始化的数组会被填充与随机垃圾内存(旁注:已经从系统重新分配,当您在关闭程序时分配或未能mlock和/或删除内存时,这可能是一个安全风险)。这是一种设计选择,旨在加速:C语言的创造者认为不自动初始化内存更好,并且这是正确的选择。

这不是渐进性加速(因为它是O(N)),但例如,在覆盖实际关心的东西之前,您不需要首先初始化整个内存块。这相当于(伪代码)x = list(size=10000),如果可能的话。

如果您想在Python中实现类似的操作,可以使用 numpy 数值矩阵/多维数组操作包。具体而言,numpy.emptynumpy.empty_like

这就是您问题的真正答案。


“_”只是一个变量的“哑名”,在迭代范围时并不真正需要它。有时候我希望可以直接写成“for range(10)”。 - Ray
2
x = [[None]]*10是“错误的”。尝试x[0].append(1)并看看魔法。 - Amit Tripathi
@Death-Stalker:是的,我想那就是我实际上试图指出和说明的(“使用可变对象”)。但是谢谢你,我想你让我意识到我的答案措辞很糟糕。已修正。 - ninjagecko
xrange怎么样? - James

16
你可以使用这个代码:[None] * 10。但是这不会是“固定大小”,你仍然可以添加、删除... 这就是列表的制作方式。
你可以将其转换为元组(tuple([None] * 10))以固定宽度,但同样地,你将无法更改它(并非所有情况,只有存储的项目为可变时)。
另一个更接近你要求的选择不是列表,而是具有最大长度的collections.deque。它是最大尺寸,但它可以更小。
import collections
max_4_items = collections.deque([None] * 4, maxlen=4)

不过,只需使用列表,并习惯于“Pythonic”做事的方式。


1
注意,deque 不允许你从列表中间弹出一个元素。 - Jonathan

11

这更像是一个警告而不是一个答案。
在看到其他答案中my_list = [None] * 10的写法后,我也尝试了一下用speakers = [['','']] * 10这样的方式生成一个数组,但最终结果并不如预期地运行。
最终我做出了以下修改:

speakers = []
for i in range(10):
    speakers.append(['',''])

[['','']] * 10 看起来创建了一个 list,其中后续元素是第一个元素的副本。
例如:

>>> n=[['','']]*10
>>> n
[['', ''], ['', ''], ['', ''], ['', ''], ['', ''], ['', ''], ['', ''], ['', ''], ['', ''], ['', '']]
>>> n[0][0] = "abc"
>>> n
[['abc', ''], ['abc', ''], ['abc', ''], ['abc', ''], ['abc', ''], ['abc', ''], ['abc', ''], ['abc', ''], ['abc', ''], ['abc', '']]
>>> n[0][1] = "True"
>>> n
[['abc', 'True'], ['abc', 'True'], ['abc', 'True'], ['abc', 'True'], ['abc', 'True'], ['abc', 'True'], ['abc', 'True'], ['abc', 'True'], ['abc', 'True'], ['abc', 'True']]

相对于使用.append选项:

>>> n=[]
>>> for i in range(10):
...  n.append(['',''])
... 
>>> n
[['', ''], ['', ''], ['', ''], ['', ''], ['', ''], ['', ''], ['', ''], ['', ''], ['', ''], ['', '']]
>>> n[0][0] = "abc"
>>> n
[['abc', ''], ['', ''], ['', ''], ['', ''], ['', ''], ['', ''], ['', ''], ['', ''], ['', ''], ['', '']]
>>> n[0][1] = "True"
>>> n
[['abc', 'True'], ['', ''], ['', ''], ['', ''], ['', ''], ['', ''], ['', ''], ['', ''], ['', ''], ['', '']]

我确信ninjagecko所提供的被接受的答案试图提到这一点,但可惜我太蠢了无法理解。总之,保重!


2
[expr] * n 将会计算 expr,然后使用该值创建一个包含 n 个元素的列表。需要注意的是,expr 只会被计算一次。如果 expr 计算出来的是不可变值,那么这种方式就没问题了。但如果它计算出来的是可变值,那么每个元素都会指向同一个对象,这显然不是我们想要的结果。在这种情况下,你需要让 expr 对每个元素都计算一次。Pythonic 的解决方案是 [expr for _ in range(n)],所以在这个例子中应该写成 [['',''] for _ in range(10)] - Kevin

8
你可以使用array模块来完成。array模块是Python标准库的一部分。
from array import array
from itertools import repeat

a = array("i", repeat(0, 10))
# or
a = array("i", [0]*10)

repeat函数会将0重复10次。它比[0]*10更加节省内存,因为它不会分配内存,而是重复返回同一个数字x次。


4

这种初始化列表的方式并不是Python的惯用方式。不过,你可以像这样初始化一个列表:

>>> l = [None] * 4
>>> l
[None, None, None, None]

3
请注意,当您在C ++中使用数组时,您可能有不同的需求,在Python中以不同的方式解决:
  1. 您可能只需要一个项目集合; Python列表完美地处理了这种用例。
  2. 您可能需要一个适当的数组,包含同类型的项目。 Python列表不是存储数组的好方法。
Python通过NumPy来解决数组的需求,其中,除其他之外,还有一种创建已知大小的数组的方法:
from numpy import *

l = zeros(10)

10
使用from numpy import *会将Python内置的函数allabsminmaxsumanyround替换为NumPy中的等效函数,这可能并非您想要的结果。请注意使用。 - Lauritz V. Thaulow
2
是的,请注意 numpy 模块包含相当多的名称(当您编写数组代码时,在模块命名空间中拥有这些名称仍然很方便)。如果可能的名称冲突给您带来麻烦,请使用限定导入。 - ulidtko

2

Python没有内置支持此功能的方法。如果您不认为添加元素会带来太多开销,那么您真的需要如此优化吗?

但是,您可以使用类似于l = [None] * 1000的方法。

或者,您可以使用生成器。


好的,我对Python的内存管理不是很熟悉,我会改变主意。谢谢~ - wtm

1
fix_array = numpy.empty(n, dtype = object)

n表示您的数组的大小。

虽然它可以工作,但这可能不是最好的想法,因为您需要导入一个库来实现此目的。希望这可以帮助到您!


1
your_list = [None]*size_required

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