Как перебирать два списка параллельно?

У меня есть две итерации в 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 xrange(len(foo)):
    print "f: ", foo[i], "; b: ", b[i]

Но это кажется мне несколько нелепым. Есть ли лучший способ сделать это?

16 ответов

Решение
for f, b in zip(foo, bar):
    print(f, b)

zip останавливается, когда короче foo или же bar останавливается.

В Python 2 zip возвращает список кортежей. Это хорошо, когда foo а также bar не массивны. Если они оба массивные, то образуя zip(foo,bar) является излишне массивной временной переменной и должна быть заменена itertools.izip или же itertools.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 останавливается, когда либо foo или же bar исчерпан. izip_longest останавливается, когда оба foo а также bar исчерпаны. Когда более короткие итераторы будут исчерпаны, izip_longest дает кортеж с None в положении, соответствующем этому итератору. Вы также можете установить другой fillvalue Кроме того None если хочешь. Смотрите здесь для полной истории.

В Python 3 zip возвращает итератор кортежей, например itertools.izip в Python2. Чтобы получить список кортежей, используйте list(zip(foo, bar)), И чтобы архивировать до тех пор, пока оба итератора не будут исчерпаны, вы должны использовать itertools.zip_longest.


Обратите внимание, что zip И его zip Подобно тому, как 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

Вы хотите zip функция.

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

Основываясь на ответе @unutbu, я сравнил производительность итераций двух идентичных списков при использовании Python 3.6.zip() функции, Python enumerate() функцию, используя ручной счетчик (см. count() функция), используя список индексов, и во время особого сценария, когда элементы одного из двух списков (либо foo или bar) можно использовать для индексации другого списка. Их возможности для печати и создания нового списка, соответственно, были исследованы с использованиемtimeit()функция, в которой использовалось количество повторений 1000 раз. Один из скриптов Python, которые я создал для выполнения этих исследований, приведен ниже. Размерыfoo а также bar списки содержали от 10 до 1000000 элементов.

Полученные результаты:

  1. В печатных целях: характеристики всех рассмотренных подходов примерно схожи с характеристикамиzip()с учетом допуска точности +/-5%. Исключение произошло, когда размер списка был меньше 100 элементов. В таком сценарии метод index-list был немного медленнее, чемzip() функции, пока enumerate()функция была на ~9% быстрее. Другие методы показали аналогичную производительностьzip() функция.

  2. Для создания списков: были изучены два типа подходов ксозданию списков: использование (a)list.append()метод и (б)понимание списка. После факторинга допуска точности +/- 5% для обоих этих подходовzip() было обнаружено, что функция работает быстрее, чем enumerate()функция, чем использование списка-индекса, чем использование ручного счетчика. Прирост производительностиzip()функция в этих сравнениях может быть на 5–60% быстрее. Интересно, что с помощью элементаfoo индексировать bar может обеспечить эквивалентную или более высокую производительность (от 5% до 20%), чемzip()функция.

Разбираемся в этих результатах:

Программист должен определить количество времени вычислений для каждой операции, которая имеет смысл или имеет значение.

Например, для целей печати, если этот временной критерий составляет 1 секунду, то есть 10**0 секунд, то глядя на ось Y графика, которая находится слева на 1 секунде, и проецируя ее по горизонтали, пока не дойдут до кривых мономов, мы видим, что размеры списков, состоящих из более чем 144 элементов, потребуют значительных вычислительных затрат и значимости для программиста. То есть любая производительность, полученная с помощью подходов, упомянутых в этом исследовании, для списков меньшего размера будет незначительной для программиста. Программист сделает вывод, что производительностьzip() функция для итерации операторов печати аналогична другим подходам.

Вывод

Значительную производительность можно получить, используя zip() функция для параллельного перебора двух списков во время listсоздание. При параллельном просмотре двух списков для распечатки элементов двух списковzip() даст ту же производительность, что и enumerate() как для использования переменной счетчика вручную, как для использования списка индексов, так и для специального сценария, когда элементы одного из двух списков (либо foo или bar) можно использовать для индексации другого списка.

Скрипт 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()

Вы должны использовать функцию " zip". Вот пример того, как может выглядеть ваша собственная функция zip

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

Встроенный zip делает именно то, что вы хотите. Если вы хотите сделать то же самое для итераций вместо списков, вы можете посмотреть на itertools.izip, который делает то же самое, но дает результаты по одному.

То, что вы ищете, называется zip,

Вот как это сделать с пониманием списка:

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

Вы можете связать 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))

Функция zip решает проблему
Документы: функция ZIP библиотеки

ЦЕЛЬ: поставить вывод рядом. Проблема:

#value1 is a list
value1 = driver.find_elements_by_class_name("review-text")
#value2 is a list
value2 = driver.find_elements_by_class_name("review-date")

for val1 in value1:
    print(val1.text)
    print "\n"
for val2 in value2:
    print(val2.text)
    print "\n"

Выход:
review1
review2
обзоре3
date1
date2
date3

Решение:

for val1, val2 in zip(value1,value2):
    print (val1.text+':'+val2.text)
    print "\n"

Выход:
review1: date1
review2: date2
обзоре3:date3

почему мы не можем просто использовать индекс для итерации ..

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

Если вы хотите сохранить индексы при использовании для повторения нескольких списков вместе, вы можете передатьzipВозражатьenumerate():

      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()повторяется до тех пор, пока не закончится кратчайший список. Если вы хотите повторять до тех пор, пока не закончится самый длинный список, используйтеzip_longestиз встроенногоitertoolsмодуль. Он дополняет пропущенные значенияNoneпо умолчанию (но вы можете изменить его на любое значение с помощьюfillvalueпараметр).

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

Если кто-то ищет что-то подобное, мне это показалось очень простым и легким:

list_1 = ["Hello", "World"]
list_2 = [1, 2, 3]

for a,b in [(list_1, list_2)]:
    for element_a in a:
        ...
    for element_b in b:
        ...

>> Hello
World
1
2
3

Списки будут повторяться с полным содержимым, в отличие от zip(), который выполняет итерацию только до минимальной длины содержимого.

Вы можете использовать 3 типа в одном словаре:

def construct_dictionary_from_lists(names, ages, scores):
     end_str_dic = {}
     for item_name, item_age, score_item in zip(names, ages, scores):
         end_str_dic[item_name] = item_age, score_item
     return end_str_dic


print(
        construct_dictionary_from_lists(
            ["paul", "saul", "steve", "chimpy"],
            [28, 59, 22, 5], 
            [59, 85, 55, 60]
         )
      )

Make a zip object which makes the iterative list of tuples out of the list's element one by one.

like this [ (arr1[0],arr2[0]), (arr1[1],arr2[1]), ....]

      result=zip(arr1,arr2)
for res in result:
     print(res[0],res[1])

happy coding.

Python 3:

      import itertools as it

for foo, bar in list(it.izip_longest(list1, list2)):
    print(foo, bar)
def ncustom_zip(seq1,seq2,max_length):
        length= len(seq1) if len(seq1)>len(seq2) else len(seq2) if max_length else len(seq1) if len(seq1)<len(seq2) else len(seq2)
        for i in range(length):
                x= seq1[i] if len(seq1)>i else None  
                y= seq2[i] if len(seq2)>i else None
                yield x,y


l=[12,2,3,9]
p=[89,8,92,5,7]

for i,j in ncustom_zip(l,p,True):
        print i,j
for i,j in ncustom_zip(l,p,False):
        print i,j
Другие вопросы по тегам