创建单项重复N次的列表

785 投票
10 回答
998193 浏览
提问于 2025-04-16 02:38

我想创建一系列列表,每个列表的长度都不一样。每个列表里都会包含同一个元素 e,这个元素会重复 n 次(其中 n 是列表的长度)。

我该怎么做才能创建这些列表,而不想用列表推导式 [e for number in xrange(n)] 来为每个列表单独写呢?

10 个回答

127

在Python中创建一个重复n次的单项列表

根据你的使用场景,你可能需要用不同的方法来处理不同的情况。

对不可变对象进行列表重复

对于不可变对象,比如None、布尔值、整数、浮点数、字符串、元组或冻结集合,你可以这样做:

[e] * 4

举个例子:

>>> [None] * 4
[None, None, None, None]

注意,这种方法通常只适用于不可变对象(如字符串、元组、冻结集合等),因为它们在内存中指向的是同一个地方。

举个使用场景,我在需要构建一个全是字符串的表格时会用到这个方法,这样我就不需要给出重复的映射了。

schema = ['string'] * len(columns)

重复包含可变状态的相同项的列表

重复一个列表会得到相同的元素多次。这个需求不太常见:

[iter(iterable)] * 4

有时这可以用来将一个可迭代对象映射成一个列表的列表:

>>> iterable = range(12)
>>> a_list = [iter(iterable)] * 4
>>> [[next(l) for l in a_list] for i in range(3)] # uninteresting usage
[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]

我们可以看到a_list包含了四次相同的范围迭代器:

>>> from pprint import pprint
>>> pprint(a_list)
[<range_iterator object at 0x7f9fe3b58420>,
 <range_iterator object at 0x7f9fe3b58420>,
 <range_iterator object at 0x7f9fe3b58420>,
 <range_iterator object at 0x7f9fe3b58420>]

可变对象

我使用Python已经很长时间了,看到的使用可变对象的场景非常少。

相反,如果你想重复一个可变的空列表、集合或字典,你应该这样做:

list_of_lists = [[] for _ in iterator_of_needed_length]

下划线在这个上下文中只是一个临时变量名。

如果你只有数字的话,可以这样:

list_of_lists = [[] for _ in range(4)]

下划线作为临时变量名并没有特别的意义,但如果你不打算使用这个变量,静态代码分析工具可能会抱怨你用其他名字。


使用乘法方法处理可变对象的注意事项:

小心使用可变对象,当你改变其中一个时,其他的也会一起改变,因为它们都是同一个对象:

foo = [[]] * 4
foo[0].append('x')

此时foo返回:

[['x'], ['x'], ['x'], ['x']]

但对于不可变对象,你可以让它工作,因为你改变的是引用,而不是对象本身:

>>> l = [0] * 4
>>> l[0] += 1
>>> l
[1, 0, 0, 0]

>>> l = [frozenset()] * 4
>>> l[0] |= set('abc')
>>> l
[frozenset(['a', 'c', 'b']), frozenset([]), frozenset([]), frozenset([])]

但是再次强调,可变对象不适合这样做,因为就地操作会改变对象,而不是引用:

l = [set()] * 4
>>> l[0] |= set('abc')    
>>> l
[set(['a', 'c', 'b']), set(['a', 'c', 'b']), set(['a', 'c', 'b']), set(['a', 'c', 'b'])]
219
>>> [5] * 4
[5, 5, 5, 5]

当你重复一个列表的时候要小心。这个列表不会被复制:所有的元素都会指向同一个列表!

>>> x=[5]
>>> y=[x] * 4
>>> y
[[5], [5], [5], [5]]
>>> y[0][0] = 6
>>> y
[[6], [6], [6], [6]]
1153

你也可以这样写:

[e] * n

需要注意的是,如果 e 比如是一个空列表,你得到的将是一个包含 n 个指向同一个空列表的引用的列表,而不是 n 个独立的空列表。

性能测试

乍一看,使用 repeat 似乎是创建一个包含 n 个相同元素的列表最快的方法:

>>> timeit.timeit('itertools.repeat(0, 10)', 'import itertools', number = 1000000)
0.37095273281943264
>>> timeit.timeit('[0] * 10', 'import itertools', number = 1000000)
0.5577236771712819

但是等等,这并不是一个公平的测试……

>>> itertools.repeat(0, 10)
repeat(0, 10)  # Not a list!!!

函数 itertools.repeat 实际上并不会创建列表,它只是创建了一个对象,如果你愿意的话,可以用这个对象来生成一个列表!让我们再试一次,但这次转换成列表:

>>> timeit.timeit('list(itertools.repeat(0, 10))', 'import itertools', number = 1000000)
1.7508119747063233

所以如果你想要一个列表,使用 [e] * n。如果你想懒惰地生成元素,使用 repeat

撰写回答