#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):])