Как перебирать два списка параллельно?
У меня есть две итерации в 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 элементов.
Полученные результаты:
В печатных целях: характеристики всех рассмотренных подходов примерно схожи с характеристиками
zip()
с учетом допуска точности +/-5%. Исключение произошло, когда размер списка был меньше 100 элементов. В таком сценарии метод index-list был немного медленнее, чемzip()
функции, покаenumerate()
функция была на ~9% быстрее. Другие методы показали аналогичную производительностьzip()
функция.Для создания списков: были изучены два типа подходов ксозданию списков: использование (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, который делает то же самое, но дает результаты по одному.
Вот как это сделать с пониманием списка:
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