如何并行迭代两个列表?

Nat*_*man 767 python iterator for-loop list

我在Python中有两个迭代,我想成对地遍历它们:

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

for (f, b) in some_iterator(foo, bar):
    print "f: ", f, "; b: ", b
Run Code Online (Sandbox Code Playgroud)

它应该导致:

f: 1; b: 4
f: 2; b: 5
f: 3; b: 6
Run Code Online (Sandbox Code Playgroud)

一种方法是迭代索引:

for i in xrange(len(foo)):
    print "f: ", foo[i], "; b: ", b[i]
Run Code Online (Sandbox Code Playgroud)

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

unu*_*tbu 1204

for f, b in zip(foo, bar):
    print(f, b)
Run Code Online (Sandbox Code Playgroud)

zip当较短foobar停止时停止.

Python 2中,zip 返回元组列表.当这是罚款itertools.izip,并list(zip(foo, bar))没有巨大的.如果它们都是大规模的,那么形成zip是一个不必要的大量临时变量,应该用foo或 替换bar,它返回迭代器而不是列表.

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)
Run Code Online (Sandbox Code Playgroud)

zip(foo,bar)当任何一个itertools.izipitertools.izip_longest用尽时停止. izip当两个停止foobar耗尽.当较短的迭代器耗尽时,在与迭代器对应的位置izip_longest产生一个元组foo.您还可以设置不同bar之外izip_longest,如果你想.请看这里的完整故事.

Python 3中,None 返回元组的迭代器,就像fillvalue在Python2中一样.要获取元组列表,请使用None.并且在两个迭代器都耗尽之前进行压缩,您将使用 itertools.zip_longest.


还要注意zip,它zip的类似brethen可以接受任意数量的iterables作为参数.例如,

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

版画

1 red manchego
2 blue stilton
3 green brie
Run Code Online (Sandbox Code Playgroud)

  • 您可能首先要提到Python 3,因为它可能更具未来性.此外,值得指出的是,在Python 3中,zip()具有的优势是只有itertools.izip()在Python 2中具有这种优势,因此它通常是要走的路. (3认同)
  • 我可以请你更新你的答案,明确说明`itertools`中的`zip`和`zip`-like函数接受任意数量的iterables而不仅仅是2?这个问题现在是规范的,你的答案是唯一值得更新的答案. (2认同)
  • @CharlieParker:是的,你可以,但是你会在 enumerate(zip(foo, bar)) 中使用 `for i, (f, b)`。 (2认同)

Kar*_*tin 55

你想要这个zip功能.

for (f,b) in zip(foo, bar):
    print "f: ", f ,"; b: ", b
Run Code Online (Sandbox Code Playgroud)

  • 在Python 3.0之前,如果你有大量的元素,你会想要使用`itertools.izip`. (10认同)

Vla*_*den 13

你应该使用' zip '功能.以下是您自己的zip功能的示例

def custom_zip(seq1, seq2):
    it1 = iter(seq1)
    it2 = iter(seq2)
    while True:
        yield next(it1), next(it2)
Run Code Online (Sandbox Code Playgroud)

  • 这是对“zip”的相当有限的重新发明,而且措辞相当具有误导性。如果你要重新发明轮子(不要——它是一个内置函数,而不是依赖项),至少[这个答案](/sf/answers/4258941991/)接受一个可变的数字的可迭代对象,并且通常表现得如您所期望的“zip”。 (3认同)

Sun*_*ear 9

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

结果:

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

    打印循环 1000 次

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

    创建列表 - 1000reps

理解这些结果:

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

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

结论

zip()list创建期间使用该函数并行迭代两个列表可以获得显着的性能。当并行迭代两个列表以打印出两个列表的元素时,该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()
Run Code Online (Sandbox Code Playgroud)


Cha*_*had 6

以下是如何使用列表理解来做到这一点:

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

它打印:

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

  • [仅使用列表推导式来产生副作用是 Pythonic 吗?](/sf/ask/402751821/经理-for-just-side-effects) (10认同)

Don*_*n F 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))
Run Code Online (Sandbox Code Playgroud)