Архивирование списков неодинакового размера

#python #python-2.7

#python #структуры данных

Вопрос:

У меня есть два списка

 a = [1,2,3]
b = [9,10]
 

Я хочу объединить (zip) эти два списка в один список c таким образом, чтобы

 c = [(1,9), (2,10), (3, )]
 

Есть ли какая-либо функция в стандартной библиотеке Python для этого?

Ответ №1:

Обычно itertools.zip_longest для этого используется:

 >>> import itertools
>>> a = [1, 2, 3]
>>> b = [9, 10]
>>> for i in itertools.zip_longest(a, b): print(i)
... 
(1, 9)
(2, 10)
(3, None)
 

Но zip_longest дополняет более короткую итерацию None s (или любым значением, которое вы передаете в качестве fillvalue= параметра). Если это не то, что вы хотите, вы можете использовать понимание для фильтрации None :

 >>> for i in (tuple(p for p in pair if p is not None) 
...           for pair in itertools.zip_longest(a, b)):
...     print(i)
... 
(1, 9)
(2, 10)
(3,)
 

но обратите внимание, что если какой-либо из итераций имеет None значения, это также отфильтрует их. Если вы этого не хотите, определите свой собственный объект для fillvalue= и отфильтруйте его вместо None :

 sentinel = object()

def zip_longest_no_fill(a, b):
    for i in itertools.zip_longest(a, b, fillvalue=sentinel):
        yield tuple(x for x in i if x is not sentinel)

list(zip_longest_no_fill(a, b))  # [(1, 9), (2, 10), (3,)]

 

Ответ №2:

Другой способ map :

 a = [1, 2, 3]
b = [9, 10]
c = map(None, a, b)
 

Хотя это тоже будет содержать (3, None) вместо (3,) . Чтобы сделать это, вот забавная строка:

 c = (tuple(y for y in x if y is not None) for x in map(None, a, b))
 

Комментарии:

1. x if None not in x else tuple(y for y in x if y is not None) . x if None not in x Здесь избыточно, так как об else этом позаботится. В худшем случае else будет возвращен пустой кортеж. Кроме того, если None в каком-либо кортеже if есть a, он уничтожит этот кортеж и передаст его else

2. @inspectorG4dget: Спасибо. Но теперь это не так весело: D

3. Хорошо работает в Python 2.7, но не в 3.5. Для использования в 3.5 itertools.zip_longest .

Ответ №3:

Не так уж сложно просто написать явный Python для выполнения желаемой операции:

 def izip_short(a, b):
    ia = iter(a)
    ib = iter(b)
    for x in ia:
        try:
            y = next(ib)
            yield (x, y)
        except StopIteration:
            yield (x,)
            break
    for x in ia:
        yield (x,)
    for y in ib:
        yield (None, y)

a = [1, 2, 3]
b = [9, 10]
list(izip_short(a, b))
list(izip_short(b, a))
 

Я не был уверен, как вы хотели бы обрабатывать b последовательность, которая длиннее a последовательности, поэтому в этом случае я просто вставляю a None для первого значения в кортеже.

Получите явный итератор для каждой последовательности. Запустите a итератор как for цикл, используя при этом вручную next(ib) для получения следующего значения из b последовательности. Если мы получаем a StopIteration в b последовательности, мы прерываем цикл, а затем for x in ia: получаем остальную часть a последовательности; после этого for y in ib: ничего не будет делать, потому что этот итератор уже исчерпан. В качестве альтернативы, если первый for x in ia: цикл исчерпывает a итератор, второй for x in ia: ничего не делает, но в последовательности могут остаться значения b , и for y in ib: цикл их собирает.

Комментарии:

1. itertools.zip_longest может обрабатывать более 2 аргументов, в отличие от этого кода.

2. Я не помню, о чем я думал в 2012 году, когда писал это, но я думаю, что, вероятно, я просто хотел решить проблему. Я всегда чувствовал, что если есть библиотечная функция, которая делает то, что вам нужно, вы почти никогда не захотите писать свою собственную вместо того, чтобы использовать ее. Используйте библиотечную функцию.

Ответ №4:

Одна строка:

c = zip(a, b) [(x,) for x in a[len(b):]] [(x,) for x in b[len(a):]]

Если вы хотите повторно использовать это:

 def mergeUsNicely(a, b):
    def tupleMe(val):
        return (val,)
    return zip(a, b)   map(tupleMe, a[len(b):])   map(tupleMe, b[len(a):])