Что такое питоновский способ одновременного перебора двух массивов?

Если у меня есть два массива одинаковой длины - скажем 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)]