Есть ли функция, похожая на молнию, которая заполняет до наибольшей длины?

#python

Вопрос:

Есть ли встроенная функция, которая работает так zip() , но которая будет дополнять результаты так, чтобы длина результирующего списка была длиной самого длинного ввода, а не самого короткого ввода?

 >>> a = ['a1']
>>> b = ['b1', 'b2', 'b3']
>>> c = ['c1', 'c2']

>>> zip(a, b, c)
[('a1', 'b1', 'c1')]

>>> What command goes here?
[('a1', 'b1', 'c1'), (None, 'b2', 'c2'), (None, 'b3', None)]
 

Ответ №1:

В Python 3 вы можете использовать itertools.zip_longest

 >>> list(itertools.zip_longest(a, b, c))
[('a1', 'b1', 'c1'), (None, 'b2', 'c2'), (None, 'b3', None)]
 

Вы можете указать другое значение, чем при использовании параметра None fillvalue :

 >>> list(itertools.zip_longest(a, b, c, fillvalue='foo'))
[('a1', 'b1', 'c1'), ('foo', 'b2', 'c2'), ('foo', 'b3', 'foo')]
 

С Python 2 вы можете использовать либо itertools.izip_longest (Python 2.6 ), либо использовать map с None . Это малоизвестная функция map (но map измененная в Python 3.x, поэтому она работает только в Python 2.x).

 >>> map(None, a, b, c)
[('a1', 'b1', 'c1'), (None, 'b2', 'c2'), (None, 'b3', None)]
 

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

1. Разве у нас нет решения на Python 3, отличного от itertools?

2. @PascalvKooten это не требуется. itertools в любом случае, это встроенный модуль C.

Ответ №2:

Для Python 2.6 x используйте itertools модули izip_longest .

Для Python 3 используйте zip_longest вместо этого (без ведущего i ).

 >>> list(itertools.izip_longest(a, b, c))
[('a1', 'b1', 'c1'), (None, 'b2', 'c2'), (None, 'b3', None)]
 

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

1. В случае, если вы хотите сделать свой код совместимым как с python 2, так и с python 3, вы можете использовать six.moves.zip_longest вместо этого.

Ответ №3:

решение на Python 3 без итераций:

 def zip_longest(*lists):
    def g(l):
        for item in l:
            yield item
        while True:
            yield None
    gens = [g(l) for l in lists]    
    for _ in range(max(map(len, lists))):
        yield tuple(next(g) for g in gens)
 

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

1. это работает в micropython, спасибо @dansalmo !

Ответ №4:

non itertools Мое решение на Python 2:

 if len(list1) < len(list2):
    list1.extend([None] * (len(list2) - len(list1)))
else:
    list2.extend([None] * (len(list1) - len(list2)))
 

Ответ №5:

В дополнение к принятому ответу, если вы работаете с итерациями, которые могут быть разной длины, но не должны быть, рекомендуется перейти strict=True к zip() (поддерживается начиная с Python 3.10).

Чтобы процитировать документацию:

zip() часто используется в тех случаях, когда предполагается, что итеративные объекты имеют одинаковую длину. В таких случаях рекомендуется использовать эту strict=True опцию. Его выход такой же, как и у обычного zip() :

 >>> list(zip(('a', 'b', 'c'), (1, 2, 3), strict=True))
[('a', 1), ('b', 2), ('c', 3)]
 

В отличие от поведения по умолчанию, он проверяет,
идентичны ли длины итераций, вызывая a ValueError , если это
не так:

 >>> list(zip(range(3), ['fee', 'fi', 'fo', 'fum'], strict=True))
Traceback (most recent call last):
...
ValueError: zip() argument 2 is longer than argument 1
 

Без strict=True аргумента любая ошибка
, приводящая к итерациям разной длины, будет отключена, что,
возможно, проявится как труднодоступная ошибка в другой части
программы.

Ответ №6:

Я использую 2d-массив, но концепция аналогична использованию python 2.x:

 if len(set([len(p) for p in printer])) > 1:
    printer = [column ['']*(max([len(p) for p in printer])-len(column)) for column in printer]
 

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

1. Пожалуйста, добавьте объяснение того, почему этот код работает. Или почему это правильный ответ