Вопрос-Ответ

Transpose/Unzip Function (inverse of zip)?

Функция транспонирования / разархивирования (обратная zip)?

У меня есть список кортежей из 2 элементов, и я хотел бы преобразовать их в 2 списка, где первый содержит первый элемент в каждом кортеже, а второй список содержит второй элемент.

Например:

original = [('a', 1), ('b', 2), ('c', 3), ('d', 4)]
# and I want to become...
result = (['a', 'b', 'c', 'd'], [1, 2, 3, 4])

Есть ли встроенная функция, которая это делает?

Переведено автоматически
Ответ 1

В 2.x zip является собственной функцией, обратной zip! При условии, что вы используете специальный оператор *.

>>> zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4)])
[('a', 'b', 'c', 'd'), (1, 2, 3, 4)]

Это эквивалентно вызову zip с каждым элементом списка в качестве отдельного аргумента:

zip(('a', 1), ('b', 2), ('c', 3), ('d', 4))

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

В 3.x, zip возвращает отложенный итератор, но это тривиально преобразовано:

>>> list(zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4)]))
[('a', 'b', 'c', 'd'), (1, 2, 3, 4)]
Ответ 2

Вы также могли бы сделать

result = ([ a for a,b in original ], [ b for a,b in original ])

Это должно лучше масштабироваться. Особенно если Python преуспевает в том, чтобы не расширять понимание списка без необходимости.

(Кстати, она создает списки из двух кортежей (пары), а не список кортежей, как это делает zip.)

Если генераторы вместо реальных списков в порядке, это могло бы сделать это:

result = (( a for a,b in original ), ( b for a,b in original ))

Генераторы не просматривают список, пока вы не запросите каждый элемент, но, с другой стороны, они сохраняют ссылки на исходный список.

Ответ 3

Мне нравится использовать zip(*iterable) (это тот фрагмент кода, который вы ищете) в моих программах таким образом:

def unzip(iterable):
return zip(*iterable)

Я нахожу unzip более читабельной.

Ответ 4

Если у вас есть списки разной длины, вы можете не захотеть использовать zip в соответствии с ответом Патрика. Это работает:

>>> zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4)])
[('a', 'b', 'c', 'd'), (1, 2, 3, 4)]

Но в списках разной длины zip обрезает каждый элемент до длины самого короткого списка:

>>> zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', )])
[('a', 'b', 'c', 'd', 'e')]

Вы можете использовать map без функции, чтобы заполнить пустые результаты значением None:

>>> map(None, *[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', )])
[('a', 'b', 'c', 'd', 'e'), (1, 2, 3, 4, None)]

однако zip() работает незначительно быстрее.

python list