是否可以将Python列表中的所有值设置为0,而无需逐个迭代列表和值?
我有一个列表[[0, 2, 4, 5], [0, 2, 4, 5]]
,我想将其更改为[[0, 0, 0, 0], [0, 0, 0, 0]]
。有没有办法在不循环所有值的情况下实现这一目标,是否会导致性能提升?实现这一目标的最快方法是什么,因为这段代码将被执行很多次?
列表是否已就地修改或完全替换也无关紧要。外部列表的长度非常大,而内部列表的长度很小。
答案 0 :(得分:4)
不,没有办法避免循环,因为列表具有任意大小。您还希望避免以共享的单个嵌套列表结束,因此外部列表的乘法已经完成。
以下是相当有效的,并产生一个明智的结果:
[[0] * len(inner) for inner in outer]
这将为outer
的任何长度产生正确的结果,即使嵌套列表的长度不同。
这也是跨越不同场景的最快方法,如以下时间试验所示。首先要测试的设置是:
>>> from timeit import timeit
>>> import random
>>> short_fixed = [[random.randint(0, 10) for _ in range(5)] for _ in range(10)]
>>> long_fixed = [[random.randint(0, 10) for _ in range(5)] for _ in range(1000000)]
>>> short_ranging = [[random.randint(0, 10) for _ in range(random.randrange(25))] for _ in range(10)]
>>> long_ranging = [[random.randint(0, 10) for _ in range(random.randrange(25))] for _ in range(1000000)]
我正在测试Python 3.6.1rc1上的timeit
module,在运行OS X 10.12.3的MacBook Pro(Retina,15英寸,2015年中)上进行测试
然后是每个场景。 Short fixed是一个包含10个嵌套列表的列表,每个列表长5个元素。测试时间是100万次重复的总数:
>>> timeit('list(map(lambda x:[0]*len(x),l))', 'from __main__ import short_fixed as l')
3.2795075319882017
>>> timeit('list(map(lambda x: list(repeat(0, len(x))), l))', 'from __main__ import short_fixed as l; from itertools import repeat')
6.128518687008182
>>> timeit('[[0] * len(inner) for inner in l]', 'from __main__ import short_fixed as l')
2.254983870021533
长期固定测试100万个元素,10个重复以保持等待可管理:
>>> timeit('list(map(lambda x:[0]*len(x),l))', 'from __main__ import long_fixed as l', number=10)
3.955955935991369
>>> timeit('list(map(lambda x: list(repeat(0, len(x))), l))', 'from __main__ import long_fixed as l; from itertools import repeat', number=10)
6.772360901988577
>>> timeit('[[0] * len(inner) for inner in l]', 'from __main__ import long_fixed as l', number=10)
3.302304288983578
不同的列表大小介于0到25个元素之间。短名单:
>>> timeit('list(map(lambda x:[0]*len(x),l))', 'from __main__ import short_ranging as l')
3.155180420988472
>>> timeit('list(map(lambda x: list(repeat(0, len(x))), l))', 'from __main__ import short_ranging as l; from itertools import repeat')
6.213294043001952
>>> timeit('[[0] * len(inner) for inner in l]', 'from __main__ import short_ranging as l')
2.3255828430119436
最后有100万个测距列表:
>>> timeit('list(map(lambda x: list(repeat(0, len(x))), l))', 'from __main__ import long_ranging as l; from itertools import repeat', number=10)
8.005676712986315
>>> timeit('list(map(lambda x: list(repeat(0, len(l[0]))), l))', 'from __main__ import long_ranging as l; from itertools import repeat', number=10)
8.49916388199199
>>> timeit('[[0] * len(inner) for inner in l]', 'from __main__ import long_ranging as l', number=10)
3.8087494230130687
在所有情况下,显式循环更快(最多2倍),因为它不必使用lambda函数。
如果您准备切换到numpy阵列,那么该选项可以轻松地将所有内容从水中吹走。在数组中的所有(本机)值上传播乘以0可将所有迭代移动到C,而无需调用函数或执行Python字节码:
>>> import numpy
>>> short_fixed_np = numpy.array(short_fixed)
>>> long_fixed_np = numpy.array(long_fixed)
>>> short_ranging_np = numpy.array(short_ranging)
>>> long_ranging_np = numpy.array(long_ranging)
>>> timeit('l = next(copies); l *= 0', 'from __main__ import short_fixed_np as arr, numpy; copies = iter([numpy.copy(arr) for _ in range(10**6)])')
0.8011195910221431
>>> timeit('l = next(copies); l *= 0', 'from __main__ import long_fixed_np as arr, numpy; copies = iter([numpy.copy(arr) for _ in range(10)])', number=10)
0.04912398199667223
(因为这种方法就地改变了对象,你需要为每个重复的测试创建足够的副本来改变一个独特的数组,因此整个next(copies)
舞蹈。)
将numpy数组用于其全部强度也意味着您只能将它们实际用于固定长度的子列表。对于可变长度子列表,您必须使用对象类型的单维数组(意味着它们仅用于引用Python列表),此时您不能再将乘法广播到所有数字元素。
考虑到在这种情况下你必须重新整理你的整个项目才能利用numpy数组。如果你需要从这样的数组中大量访问各个值,那么考虑到它将更慢,因为访问单个值需要每次在Python对象中装入C本机值。
答案 1 :(得分:2)
如果没有显式循环,这是一个使用itertools.repeat()
和map()
的功能方法:
In [6]: lst = list(map(lambda x: list(repeat(0, len(x))), lst))
Out[6]: [[0, 0, 0, 0], [0, 0, 0, 0]]
或者,如果您的子列表长度相同,则可以使用两个repeat()
:
In [24]: lst = list(repeat(list(repeat(0, len(lst[0]))), len(lst)))
Out[24]: [[0, 0, 0, 0], [0, 0, 0, 0]]
请注意,此方法使用循环创建重复对象并将生成器转换为列表。这意味着没有循环就无法创建像这样的单独对象。
另一种就地更改项目的方法是使用Numpy。您只需乘以0即可完成此操作:
In [18]: import numpy as np
In [19]: lst = np.array(lst)
In [21]: lst *= 0
In [22]: lst
Out[22]:
array([[0, 0, 0, 0],
[0, 0, 0, 0]])
答案 2 :(得分:0)
据我所知,不可能避免循环,但你说显式循环我想你想要一种没有for
循环的方法。
所以你可以试试这个:
print map(lambda x:[0]*len(x),l)
Python 3.x,它应该是:
print (list(map(lambda x:[0]*len(x),l)))
Map:
将函数应用于iterable的每个项目并返回一个列表 结果
答案 3 :(得分:-3)
您可以使用以下方法更新列表(没有明确的for循环):
list_of_lists = [[0] * 4] * 2
我们试一试:
>>> [[0] * 4] *2
[[0, 0, 0, 0], [0, 0, 0, 0]]