Что такое питоновский способ одновременного перебора двух массивов?
Если у меня есть два массива одинаковой длины - скажем a
и b
a = [4,6,2,6,7,3,6,7,2,5]
b = [6,4,6,3,2,7,8,5,3,5]
нормально, я бы сделал это следующим образом:
for i in range(len(a)):
print a[i] + b[i]
а не что-то вроде этого:
i=0
for number in a:
print number + b[i]
i += 1
потому что я предпочитаю быть совместимым с используемыми методами.
Я знаю zip
, но я никогда не использую его. Для этого был создан zip
?
будет
for pair in zip(a,b):
print pair[0] + pair[1]
- это питонический способ сделать это?
Ответы
Ответ 1
Если списки a
и b
являются короткими, используйте zip (как показал @Vincenzo Pii):
for x, y in zip(a, b):
print(x + y)
Если списки a
и b
длинны, используйте itertools.izip для сохранения памяти:
import itertools as IT
for x, y in IT.izip(a, b):
print(x + y)
zip
создает список кортежей. Это может быть обременительным (по памяти), если a
и b
являются большими.
itertools.izip
возвращает итератор. Итератор не генерирует полный список кортежей; он дает только каждый элемент, поскольку он запрашивается для цикла. Таким образом, он может сэкономить вам некоторую память.
В Python2 вызов zip(a,b)
в коротких списках выполняется быстрее, чем при использовании itertools.izip(a,b)
. Но в Python3 обратите внимание, что zip
возвращает итератор по умолчанию (т.е. Он эквивалентен itertools.izip
в Python2).
Другие интересующие вас варианты:
Ответ 2
Возможное решение использует zip
, как вы упомянули сами, но несколько иначе, чем то, как вы написали его в вопросе:
for x, y in zip(a, b):
print x, y
Обратите внимание, что длина списка кортежей, возвращаемых zip()
, будет равна минимуму между длинами a
и b
. Это влияет, когда a
и b
не имеют одинаковой длины.
Ответ 3
Вместо использования zip
вы можете использовать Numpy, особенно если скорость важна, и у вас есть длинные массивы. Это намного быстрее, и когда вы используете массивы numpy, вам не нужен цикл, и вы можете просто написать:
print a + b
График, показывающий усредненные тайминги для суммирования списков различной длины с использованием zip, izip и numpy:
![enter image description here]()
Ответ 4
Предлагая этот ответ для полноты, поскольку numpy
обсуждался в другом ответе, и часто полезно сочетать значения вместе с более ранжированными массивами.
Принимаемый ответ отлично подходит для любой последовательности/массива ранга 1. Однако, если последовательность имеет несколько уровней (например, массив numpy
ранга 2 или более, но также, например, в list
of list
s или tuple
of tuple
s), необходимо выполнить итерацию по каждому рангу. Ниже приведен пример с массивом 2D numpy
:
import numpy as np
a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
b = np.array([list('abc'), list('pdq'), list('xyz')])
c = np.array([[frobnicate(aval, bval) for aval, bval in zip(arow, brow)] for arow, brow in zip(a, b)])
И та же концепция будет работать для любого набора двумерных вложенных последовательностей одной и той же формы:
a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
b = [list('abc'), list('pdq'), list('xyz')]
c = [[frobnicate(aval, bval) for aval, bval in zip(arow, brow)] for arow, brow in zip(a, b)]
Если у одной или обеих вложенных последовательностей есть "дыры" в ней, используйте itertools.zip_longest
, чтобы заполнить отверстия (заполнить значение по умолчанию равно None
, но может быть указано):
from itertools import zip_longest as zipl
a = [[], [4, 5, 6], [7, 8, 9]] # empty list in the first row
b = [list('abc'), list('pdq'), []] # empty list in the last row
c = [[frobnicate(aval, bval) for aval, bval in zipl(arow, brow)] for arow, brow in zipl(a, b)]