web-dev-qa-db-fra.com

Quelle est la difference entre chain et chain.from_iterable dans itertools?

Je n'ai trouvé aucun exemple valable sur Internet où je puisse voir la différence et pourquoi choisir l'un par rapport à l'autre.

50
user1994660

Le premier prend 0 ou plusieurs arguments, chacun un itératif, le second prend un argument qui devrait produire les itérables:

itertools.chain(list1, list2, list3)

iterables = [list1, list2, list3]
itertools.chain.from_iterable(iterables)

mais iterables peut être n’importe quel itérateur qui donne les itérables.

def generate_iterables():
    for i in range(10):
        yield range(i)

itertools.chain.from_iterable(generate_iterables())

L'utilisation de la seconde forme est généralement un cas de commodité, mais comme elle parcourt les iterables en entrée, c'est aussi le seul moyen de chaîner un nombre infinite d'itérateurs finis:

def generate_iterables():
    while True:
        for i in range(5, 10):
            yield range(i)

itertools.chain.from_iterable(generate_iterables())

L'exemple ci-dessus vous donnera une valeur itérable qui produit un motif cyclique de nombres qui ne s'arrêteront jamais, mais ne consommeront jamais plus de mémoire que ce qu'un simple appel range() nécessite.

61
Martijn Pieters

Ils font des choses très similaires. itertools.chain(*iterables) et itertools.chain.from_iterable(iterables) fonctionnent de manière similaire pour un petit nombre de iterables.

Le principal avantage de from_iterables réside dans la possibilité de gérer un grand nombre (potentiellement infini) de données itérables, car elles ne doivent pas toutes être disponibles au moment de l'appel.

4
BiGYaN

Une autre façon de le voir: 

chain(iterable1, iterable2, iterable3, ...) est pour quand vous savez déjà quels iterables vous avez, ainsi vous pouvez les écrire en tant que ces arguments séparés par des virgules. 

chain.from_iterable(iterable) est pour quand vos iterables (comme iterable1, iterable2, iterable3) sont obtenus à partir d'un autre iterable.

1
R. Navega

Je n'ai trouvé aucun exemple valide ... où je peux voir la différence entre eux [chain et chain.from_iterable] et pourquoi choisir l'un plutôt que l'autre

La réponse acceptée est approfondie. Pour ceux qui recherchent une application rapide, envisagez d’aplatir plusieurs listes:

list(itertools.chain(["a", "b", "c"], ["d", "e"], ["f"]))
# ['a', 'b', 'c', 'd', 'e', 'f']

Vous souhaiterez peut-être réutiliser ces listes ultérieurement, de sorte que vous en fassiez une liste: 

iterable = (["a", "b", "c"], ["d", "e"], ["f"])

Tentative

Cependant, le passage d'un itératif à chain donne un résultat non aplati:

list(itertools.chain(iterable))
# [['a', 'b', 'c'], ['d', 'e'], ['f']]

Pourquoi? Vous avez passé dans un élément one (un tuple). chain a besoin de chaque liste séparément.


Solutions

Lorsque cela est possible, vous pouvez décompresser un itérable:

list(itertools.chain(*iterable))
# ['a', 'b', 'c', 'd', 'e', 'f']

list(itertools.chain(*iter(iterable)))
# ['a', 'b', 'c', 'd', 'e', 'f']

Plus généralement, utilisez .from_iterable (car cela fonctionne aussi avec des itérateurs infinis):

list(itertools.chain.from_iterable(iterable))
# ['a', 'b', 'c', 'd', 'e', 'f']

g = itertools.chain.from_iterable(itertools.cycle(iterable))
next(g)
# "a"
0
pylang