Conozco la función zip (que se comprimirá de acuerdo con la lista más corta) y zip_longest (que se comprimirá de acuerdo con la lista más larga), pero ¿cómo puedo comprimir de acuerdo con la primera lista, independientemente de si es la más larga o no?
Por ejemplo:
Input: ['a', 'b', 'c'], [1, 2] Output: [('a', 1), ('b', 2), ('c', None)]Pero también:
Input: ['a', 'b'], [1, 2, 3] Output: [('a', 1), ('b', 2)]¿Existen ambas funcionalidades en una función?
Puede reutilizar el código python "más o menos equivalente" que se muestra en los documentos para itertools.zip_longest para hacer una versión generalizada que se comprima según la longitud del primer argumento:
from itertools import repeat def zip_by_first(*args, fillvalue=None): # zip_by_first('ABCD', 'xy', fillvalue='-') --> Ax By C- D- # zip_by_first('ABC', 'xyzw', fillvalue='-') --> Ax By Cz if not args: return iterators = [iter(it) for it in args] while True: values = [] for i, it in enumerate(iterators): try: value = next(it) except StopIteration: if i == 0: return iterators[i] = repeat(fillvalue) value = fillvalue values.append(value) yield tuple(values) Es posible que pueda realizar algunas pequeñas mejoras, como almacenar en caché repeat(fillvalue) o menos. El problema con esta implementación es que está escrita en Python, mientras que la mayoría de itertools usa una implementación C mucho más rápida. Puede ver los efectos de esto comparándolo con la respuesta de Kelly Bundy .
Aquí hay otra toma, si el objetivo es un código legible y fácil de entender:
def zip_first(first, *rest, fillvalue=None): rest = [iter(r) for r in rest] for x in first: yield x, *(next(r, fillvalue) for r in rest) Esto usa la forma de dos argumentos de next() para devolver el valor de relleno para todos los iterables que están agotados.
Para exactamente dos iterables, esto se puede simplificar a
def zip_first(first, second, fillvalue=None): second = iter(second) for x in first: yield x, next(second, fillvalue)