创建单项重复N次的列表
我想创建一系列列表,每个列表的长度都不一样。每个列表里都会包含同一个元素 e
,这个元素会重复 n
次(其中 n
是列表的长度)。
我该怎么做才能创建这些列表,而不想用列表推导式 [e for number in xrange(n)]
来为每个列表单独写呢?
10 个回答
在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'])]
>>> [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]]
你也可以这样写:
[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
。