不,没有办法避免循环,因为列表的大小是任意的。您还希望避免以共享的单个嵌套列表结束,因此外部列表的乘法被排除在外。
以下是相当有效的,并产生一个理智的结果:
[[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)]
我在运行 OS X 10.12.3 的 MacBook Pro(Retina,15 英寸,2015 年中)上使用 Python 3.6.1rc1 上的 timeit module 进行测试
然后是每个场景。短固定是 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 数组也意味着您实际上只能将它们用于固定长度的子列表。对于可变长度子列表,您必须使用 object 类型的单维数组(意味着它们仅用于引用 Python 列表),此时您也不能再将乘法广播到所有数字元素。
考虑到在这种情况下,您必须重组整个项目才能利用 numpy 数组。如果您需要大量访问此类数组中的单个值,请考虑到这会更慢,因为访问单个值需要每次将 C 本机值装箱到 Python 对象中。 p>