如何并行遍历两个列表?

How do I iterate through two lists in parallel?

提问人:Nathan Fellman 提问时间:11/3/2009 最后编辑:Mateen UlhaqNathan Fellman 更新时间:5/19/2023 访问量:1213947

问:

我有两个可迭代对象,我想成对地回顾它们:

foo = [1, 2, 3]
bar = [4, 5, 6]

for (f, b) in iterate_together(foo, bar):
    print("f:", f, " |  b:", b)

这应该导致:

f: 1  |  b: 4
f: 2  |  b: 5
f: 3  |  b: 6

一种方法是遍历索引:

for i in range(len(foo)):
    print("f:", foo[i], " |  b:", bar[i])

但这对我来说似乎有点不蟒蛇。有没有更好的方法?


相关任务:
* 如何将列表合并到元组列表中? - 给定上面的 foobar,创建列表 [(1, 4), (2, 5), (3, 6)]。
* 如何从单独的键和值列表中制作字典(字典)? - 创建字典 {1: 4, 2: 5, 3: 6}
* 创建一个带有理解的字典 - 在字典推导式中使用 zip造字典

python list for 循环 迭代器

评论


答:

96赞 Karl Guertin 11/3/2009 #1

你想要这个函数。zip

for (f,b) in zip(foo, bar):
    print "f: ", f ,"; b: ", b
1941赞 unutbu 11/3/2009 #2

Python 3 (英语)

for f, b in zip(foo, bar):
    print(f, b)

zip当 或 中的较短者停止时停止。foobar

Python 3 中,zip 返回元组的迭代器,就像在 Python2 中一样。获取列表 的元组,请使用 .并压缩直到两个迭代器都 筋疲力尽,您将使用 itertools.zip_longestitertools.iziplist(zip(foo, bar))

Python 2 中文文档

Python 2 中,zip 返回元组列表。这在不大的时候很好。如果它们都是巨大的,那么形成就是不必要的巨大 临时变量,并且应替换为 或 ,后者返回迭代器而不是列表。foobarzip(foo,bar)itertools.izipitertools.izip_longest

import itertools
for f,b in itertools.izip(foo,bar):
    print(f,b)
for f,b in itertools.izip_longest(foo,bar):
    print(f,b)

izip当 OR 耗尽时停止。 当两者都耗尽时停止。 当较短的迭代器用尽时,将生成一个元组,其位置与该迭代器相对应。如果您愿意,您还可以设置不同的其他设置。请看这里了解完整的故事foobarizip_longestfoobarizip_longestNonefillvalueNone


还要注意的是,它的 -like brethen 可以接受任意数量的可迭代对象作为参数。例如zipzip

for num, cheese, color in zip([1,2,3], ['manchego', 'stilton', 'brie'], 
                              ['red', 'blue', 'green']):
    print('{} {} {}'.format(num, color, cheese))

指纹

1 red manchego
2 blue stilton
3 green brie

评论

0赞 unutbu 12/23/2018
通常在 Python 中,越简单越快。在 CPython 中,的 for 循环和项目获取是用 C 语言实现的。相反,使用 Python 级别的 for 循环,并且每个 or 都需要 Python 级别的调用。通常,C 循环胜过 Python 循环,更少的函数调用比更多的函数调用更快。因此,直觉指向比索引更快。zip[(x[i], y[i]) for i in range(...)]x[i]y[i]__getitem__zip
0赞 unutbu 12/23/2018
当然,关于性能的一揽子陈述是愚蠢的。因此,熟悉 timeit 并亲自测试是件好事。在我的机器上进行一点时间测试证实,它比 .list(zip(x,y))[(x[i], y[i]) for i in range(1000)]x = y = list(range(1000))
0赞 Abhishek Rai 9/25/2023
呃...zip不需要列表相等吗?
23赞 Vlad Bezden 4/23/2017 #3

您应该使用“zip”功能。下面是一个示例,您自己的 zip 函数的外观

def custom_zip(seq1, seq2):
    it1 = iter(seq1)
    it2 = iter(seq2)
    while True:
        yield next(it1), next(it2)

评论

2赞 ggorlen 2/7/2021
这是一个非常有限的重新发明,措辞相当具有误导性。如果你要重新发明轮子(不要 - 它是一个内置函数,而不是依赖项),至少这个答案接受可变数量的可迭代对象,并且通常按照你的期望行事。zipzip
7赞 Chad 8/30/2019 #4

以下是使用列表推导式执行此操作的方法:

a = (1, 2, 3)
b = (4, 5, 6)
[print('f:', i, '; b', j) for i, j in zip(a, b)]

它打印:

f: 1 ; b 4
f: 2 ; b 5
f: 3 ; b 6
6赞 Don F 3/25/2020 #5

您可以使用推导式将第 n 个元素捆绑到元组或列表中,然后使用生成器函数将它们传递出去。

def iterate_multi(*lists):
    for i in range(min(map(len,lists))):
        yield tuple(l[i] for l in lists)

for l1, l2, l3 in iterate_multi([1,2,3],[4,5,6],[7,8,9]):
    print(str(l1)+","+str(l2)+","+str(l3))
24赞 Sun Bear 6/20/2020 #6

基于@unutbu的答案,我比较了两个相同列表在使用 Python 3.6 的函数、Python 的函数、使用手动计数器(参见函数)、使用索引列表以及两个列表之一的元素(或 )可用于索引另一个列表的特殊场景时的迭代性能。他们分别使用重复次数为 1000 次的函数研究了它们在打印和创建新列表方面的性能。下面给出了我为执行这些调查而创建的 Python 脚本之一。和清单的规模从10到1,000,000个要素不等。zip()enumerate()count()foobartimeit()foobar

结果:

  1. 用于打印目的:在考虑+/-5%的精度容差后,观察到所有所考虑方法的性能与函数大致相似。当列表大小小于 100 个元素时发生异常。在这种情况下,index-list 方法比函数稍慢,而函数快 ~9%。其他方法产生的性能与该函数相似。zip()zip()enumerate()zip()

    Print loop 1000 reps

  2. 对于创建列表:探讨了两种类型的列表创建方法:使用(a)方法和(b)列表推导。在对这两种方法的精度容差进行 +/-5% 的分解后,发现该函数的执行速度比使用列表索引快,比使用手动计数器更快。在这些比较中,该函数的性能提升速度可以提高 5% 到 60%。有趣的是,使用 to index 元素可以产生与函数相当或更快的性能(5% 到 20%)。list.append()zip()enumerate()zip()foobarzip()

    Creating List - 1000reps

理解这些结果:

程序员必须确定每个操作的计算时间量是有意义的还是重要的。

例如,出于打印目的,如果此时间标准为 1 秒,即 10**0 秒,然后查看左侧 1 秒处的图形的 y 轴并将其水平投影,直到它到达单项式曲线,我们看到超过 144 个元素的列表大小将对程序员产生巨大的计算成本和重要性。也就是说,对于较小的列表大小,本调查中提到的方法所获得的任何性能对程序员来说都是微不足道的。程序员将得出结论,迭代 print 语句的函数的性能与其他方法类似。zip()

结论

在创建过程中,使用该函数并行循环访问两个列表可以获得显著的性能。当并行遍历两个列表以打印出两个列表的元素时,该函数将产生与该函数相似的性能,例如使用手动计数器变量,使用索引列表,以及在两个列表之一(或)的元素可用于索引另一个列表的特殊情况下。zip()listzip()enumerate()foobar

用于调查列表创建的 Python 3.6 脚本。

import timeit
import matplotlib.pyplot as plt
import numpy as np


def test_zip( foo, bar ):
    store = []
    for f, b in zip(foo, bar):
        #print(f, b)
        store.append( (f, b) )

def test_enumerate( foo, bar ):
    store = []
    for n, f in enumerate( foo ):
        #print(f, bar[n])
        store.append( (f, bar[n]) )

def test_count( foo, bar ):
    store = []
    count = 0
    for f in foo:
        #print(f, bar[count])
        store.append( (f, bar[count]) )
        count += 1

def test_indices( foo, bar, indices ):
    store = []
    for i in indices:
        #print(foo[i], bar[i])
        store.append( (foo[i], bar[i]) )

def test_existing_list_indices( foo, bar ):
    store = []
    for f in foo:
        #print(f, bar[f])
        store.append( (f, bar[f]) )


list_sizes = [ 10, 100, 1000, 10000, 100000, 1000000 ]
tz = []
te = []
tc = []
ti = []
tii= []

tcz = []
tce = []
tci = []
tcii= []

for a in list_sizes:
    foo = [ i for i in range(a) ]
    bar = [ i for i in range(a) ]
    indices = [ i for i in range(a) ]
    reps = 1000

    tz.append( timeit.timeit( 'test_zip( foo, bar )',
                              'from __main__ import test_zip, foo, bar',
                              number=reps
                              )
               )
    te.append( timeit.timeit( 'test_enumerate( foo, bar )',
                              'from __main__ import test_enumerate, foo, bar',
                              number=reps
                              )
               )
    tc.append( timeit.timeit( 'test_count( foo, bar )',
                              'from __main__ import test_count, foo, bar',
                              number=reps
                              )
               )
    ti.append( timeit.timeit( 'test_indices( foo, bar, indices )',
                              'from __main__ import test_indices, foo, bar, indices',
                              number=reps
                              )
               )
    tii.append( timeit.timeit( 'test_existing_list_indices( foo, bar )',
                               'from __main__ import test_existing_list_indices, foo, bar',
                               number=reps
                               )
                )

    tcz.append( timeit.timeit( '[(f, b) for f, b in zip(foo, bar)]',
                               'from __main__ import foo, bar',
                               number=reps
                               )
                )
    tce.append( timeit.timeit( '[(f, bar[n]) for n, f in enumerate( foo )]',
                               'from __main__ import foo, bar',
                               number=reps
                               )
                )
    tci.append( timeit.timeit( '[(foo[i], bar[i]) for i in indices ]',
                               'from __main__ import foo, bar, indices',
                               number=reps
                               )
                )
    tcii.append( timeit.timeit( '[(f, bar[f]) for f in foo ]',
                                'from __main__ import foo, bar',
                                number=reps
                                )
                 )

print( f'te  = {te}' )
print( f'ti  = {ti}' )
print( f'tii = {tii}' )
print( f'tc  = {tc}' )
print( f'tz  = {tz}' )

print( f'tce  = {te}' )
print( f'tci  = {ti}' )
print( f'tcii = {tii}' )
print( f'tcz  = {tz}' )

fig, ax = plt.subplots( 2, 2 )
ax[0,0].plot( list_sizes, te, label='enumerate()', marker='.' )
ax[0,0].plot( list_sizes, ti, label='index-list', marker='.' )
ax[0,0].plot( list_sizes, tii, label='element of foo', marker='.' )
ax[0,0].plot( list_sizes, tc, label='count()', marker='.' )
ax[0,0].plot( list_sizes, tz, label='zip()', marker='.')
ax[0,0].set_xscale('log')
ax[0,0].set_yscale('log')
ax[0,0].set_xlabel('List Size')
ax[0,0].set_ylabel('Time (s)')
ax[0,0].legend()
ax[0,0].grid( b=True, which='major', axis='both')
ax[0,0].grid( b=True, which='minor', axis='both')

ax[0,1].plot( list_sizes, np.array(te)/np.array(tz), label='enumerate()', marker='.' )
ax[0,1].plot( list_sizes, np.array(ti)/np.array(tz), label='index-list', marker='.' )
ax[0,1].plot( list_sizes, np.array(tii)/np.array(tz), label='element of foo', marker='.' )
ax[0,1].plot( list_sizes, np.array(tc)/np.array(tz), label='count()', marker='.' )
ax[0,1].set_xscale('log')
ax[0,1].set_xlabel('List Size')
ax[0,1].set_ylabel('Performances ( vs zip() function )')
ax[0,1].legend()
ax[0,1].grid( b=True, which='major', axis='both')
ax[0,1].grid( b=True, which='minor', axis='both')

ax[1,0].plot( list_sizes, tce, label='list comprehension using enumerate()',  marker='.')
ax[1,0].plot( list_sizes, tci, label='list comprehension using index-list()',  marker='.')
ax[1,0].plot( list_sizes, tcii, label='list comprehension using element of foo',  marker='.')
ax[1,0].plot( list_sizes, tcz, label='list comprehension using zip()',  marker='.')
ax[1,0].set_xscale('log')
ax[1,0].set_yscale('log')
ax[1,0].set_xlabel('List Size')
ax[1,0].set_ylabel('Time (s)')
ax[1,0].legend()
ax[1,0].grid( b=True, which='major', axis='both')
ax[1,0].grid( b=True, which='minor', axis='both')

ax[1,1].plot( list_sizes, np.array(tce)/np.array(tcz), label='enumerate()', marker='.' )
ax[1,1].plot( list_sizes, np.array(tci)/np.array(tcz), label='index-list', marker='.' )
ax[1,1].plot( list_sizes, np.array(tcii)/np.array(tcz), label='element of foo', marker='.' )
ax[1,1].set_xscale('log')
ax[1,1].set_xlabel('List Size')
ax[1,1].set_ylabel('Performances ( vs zip() function )')
ax[1,1].legend()
ax[1,1].grid( b=True, which='major', axis='both')
ax[1,1].grid( b=True, which='minor', axis='both')

plt.show()
3赞 SuperNova 7/27/2021 #7

我们可以只使用索引来迭代...

foo = ['a', 'b', 'c']
bar = [10, 20, 30]
for indx, itm in enumerate(foo):
    print (foo[indx], bar[indx])

评论

6赞 Tomerikoo 2/8/2022
如果您实际上没有使用,为什么要使用?要么更改为,要么简单地循环为enumerateitmprint(itm, bar[index])for indx in range(len(foo))
2赞 cottontail 9/9/2022 #8

如果要在用于一起循环访问多个列表时保留索引,可以将对象传递给:zip()zipenumerate()

for i, (f, b) in enumerate(zip(foo, bar)):
    # do something

例如,如果要打印出 2 个列表中值不同的位置,可以按如下方式进行打印。

foo, bar = ['a', 'b', 'c'], ['a', 'a', 'c']

for i, (f, b) in enumerate(zip(foo, bar)):
    if f != b:
        print(f"items at index {i} are different")
    
# items at index 1 are different

如果列表的长度不同,则迭代,直到最短的列表结束。如果要循环访问,直到最长的列表结束,请使用内置模块中的zip_longest。默认情况下,它会填充缺失值(但您可以使用参数将其更改为所需的任何值)。zip()itertoolsNonefillvalue

from itertools import zip_longest
for f, b in zip_longest(foo, bar):
    # do something