【问题标题】:How to iterate through two lists in parallel?如何并行遍历两个列表?
【发布时间】:2010-12-12 10:30:24
【问题描述】:

我在 Python 中有两个可迭代对象,我想成对查看它们:

foo = (1, 2, 3)
bar = (4, 5, 6)

for (f, b) in some_iterator(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])

但这对我来说似乎有些不合时宜。有没有更好的方法?

【问题讨论】:

    标签: python list for-loop iterator


    【解决方案1】:

    你想要zip 函数。

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

    【讨论】:

    • 在 Python 3.0 之前,如果您有大量元素,您可能需要使用 itertools.izip
    【解决方案2】:

    Python 3

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

    zipfoobar 中较短者停止时停止。

    Python 3 中,zip 返回元组的迭代器,如 Python2 中的 itertools.izip。获取列表 元组,使用list(zip(foo, bar))。并压缩直到两个迭代器都 筋疲力尽,你会用 itertools.zip_longest.

    Python 2

    Python 2 中,zip 返回一个元组列表。当foobar 不是很大时,这很好。如果它们都是巨大的,那么形成zip(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)
    

    izipfoobar 用尽时停止。 当foobar 都用尽时,izip_longest 停止。 当较短的迭代器用尽时,izip_longest 会在与该迭代器对应的位置生成一个带有None 的元组。如果您愿意,除了None 之外,您还可以设置不同的fillvalue。请在此处查看full story


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

    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
    

    【讨论】:

    • @unutbu 为什么我更喜欢 OP 的方法而不是 izip 一种(即使 izip/ zip 看起来更干净)?
    • 您可能想首先提及 Python 3,因为它可能更面向未来。此外,值得指出的是,在 Python 3 中,zip() 具有只有 itertools.izip() 在 Python 2 中具有的优势,因此它通常是要走的路。
    • 我可以请您更新您的答案以明确声明来自itertoolszipzip 类函数接受任意数量的可迭代对象,而不仅仅是2?这个问题现在很规范,您的答案是唯一值得更新的问题。
    • 如果我还想要索引i 怎么办?我可以在枚举中包装那个 zip 吗?
    • @CharlieParker:是的,你可以,但你会使用for i, (f, b) in enumerate(zip(foo, bar))
    【解决方案3】:

    你应该使用'zip'函数。这是一个示例,您自己的 zip 函数的样子

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

    【讨论】:

    • 这不是和zip(seq1, seq2)的结果完全一样吗?
    • @NiklasMertsch 是的,它的结果完全相同。我刚刚提供了 zip 函数的示例
    • 这是对zip 的相当有限的改造,其措辞颇具误导性。如果你要重新发明轮子(不要——它是一个内置函数,而不是依赖项),至少 this answer 接受可变数量的迭代,并且通常表现得像你期望的 zip 那样。
    【解决方案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
    
    【解决方案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))
    

    【讨论】:

      【解决方案6】:

      基于@unutbu 的答案,我比较了两个相同列表在使用Python 3.6 的zip() 函数、Python 的enumerate() 函数、使用手动计数器时的迭代性能(参见count() 函数) ,使用索引列表,并且在两个列表之一(foobar)的元素可用于索引另一个列表的特殊情况下。分别使用timeit() 函数研究了它们在打印和创建新列表方面的性能,其中使用的重复次数为 1000 次。下面给出了我为执行这些调查而创建的 Python 脚本之一。 foobar 列表的大小范围从 10 到 1,000,000 个元素。

      结果:

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

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

      理解这些结果:

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

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

      结论

      在创建list 期间使用zip() 函数并行遍历两个列表可以获得显着的性能。当并行遍历两个列表以打印出两个列表的元素时,zip() 函数将产生与enumerate() 函数相似的性能,如使用手动计数器变量,如使用索引列表,以及至于在两个列表之一的元素(foobar)可能用于索引另一个列表的特殊情况下。

      用于调查列表创建的 Python3.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()
      

      【讨论】:

      • 几乎所有的时间都花在了你的print测试中。印刷很贵。列表构建也有一些成本。
      • @user2357112supportsMonica 同意。对于打印,迭代性能由慢速系统 I/O 操作决定,因此对 zip() 函数或我考虑的其他方法的性能不敏感。
      【解决方案7】:

      为什么我们不能只使用索引来迭代..

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        相关资源
        最近更新 更多