83 votes

Quelle est la différence entre chain et chain.from_iterable dans itertools ?

Je n'ai pu trouver aucun exemple valable sur l'internet où je puisse voir la différence entre les deux et pourquoi choisir l'un plutôt que l'autre.

94voto

Martijn Pieters Points 271458

Le premier prend 0 ou plusieurs arguments, chacun étant un itérable, le second prend un argument qui est censé produire les itérables :

from itertools import chain

chain(list1, list2, list3)

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

mais iterables peut être n'importe quel itérateur qui produit les itérables :

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

itertools.chain.from_iterable(gen_iterables())

L'utilisation de la seconde forme est généralement un cas de commodité, mais comme elle boucle sur les itérables d'entrée de façon paresseuse, c'est aussi la seule façon d'enchaîner une fonction infini nombre d'itérateurs finis :

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

chain.from_iterable(gen_iterables())

L'exemple ci-dessus vous donne un itérable qui produit un motif cyclique de nombres qui ne s'arrêtera jamais, mais qui ne consommera jamais plus de mémoire que ce qu'un simple range() demande.

15voto

pylang Points 12013

Je n'ai pas trouvé d'exemple valable ... où je puisse voir la différence entre les deux [ chain y chain.from_iterable Les deux types d'informations [ ] et les raisons de choisir l'une ou l'autre

La réponse acceptée est complète. 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']

Il se peut que vous souhaitiez réutiliser ces listes ultérieurement, c'est pourquoi vous créez un itérable de listes :

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

Tentative

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

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

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


Solutions

Lorsque c'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, utiliser .from_iterable (car il fonctionne également 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"

8voto

BiGYaN Points 1818

Ils font des choses très similaires. Pour un petit nombre d'itérables itertools.chain(*iterables) y itertools.chain.from_iterable(iterables) ont des performances similaires.

Le principal avantage de la from_iterables réside dans la capacité à gérer un grand nombre (potentiellement infini) d'itérables, puisque tous ne sont pas nécessairement disponibles au moment de l'appel.

4voto

R. Navega Points 906

Une autre façon de voir les choses :

chain(iterable1, iterable2, iterable3, ...) est utilisé lorsque vous savez déjà quels itérables vous avez, de sorte que vous pouvez les écrire en tant qu'arguments séparés par des virgules.

chain.from_iterable(iterable) est utilisé lorsque vos itérables (comme itérable1, itérable2, itérable3) sont obtenus à partir d'un autre itérable.

4voto

alvas Points 4333

Extension @martijn-pieters répondre

Bien que l'accès aux éléments internes de l'itérable reste le même, l'implémentation reste la même,

  • itertools_chain_from_iterable (c'est-à-dire chain.from_iterable en Python) et
  • chain_new (c'est-à-dire chain en Python)

dans l'implémentation CPython, sont tous deux des types de canards de chain_new_internal


Y a-t-il des avantages en termes d'optimisation dans l'utilisation de chain.from_iterable(x) donde x est un itérable d'itérables ; et le but principal est de consommer la liste d'éléments aplatie ?

Nous pouvons essayer de l'étalonner avec :

import random
from itertools import chain
from functools import wraps
from time import time

from tqdm import tqdm

def timing(f):
    @wraps(f)
    def wrap(*args, **kw):
        ts = time()
        result = f(*args, **kw)
        te = time()
        print('func:%r args:[%r, %r] took: %2.4f sec' % (f.__name__, args, kw, te-ts))
        return result
    return wrap

def generate_nm(m, n):
    # Creates m generators of m integers between range 0 to n.
    yield iter(random.sample(range(n), n) for _ in range(m))

def chain_star(x):
    # Stores an iterable that will unpack and flatten the list of list.
    chain_x = chain(*x)
    # Consumes the items in the flatten iterable.
    for i in chain_x:
        pass

def chain_from_iterable(x):
    # Stores an iterable that will unpack and flatten the list of list.
    chain_x = chain.from_iterable(x)
    # Consumes the items in the flatten iterable.
    for i in chain_x:
        pass

@timing
def versus(f, n, m):
  f(generate_nm(n, m))

P/S : Benchmark en cours... En attente des résultats.


Résultats

chain_star, m=1000, n=1000

for _ in range(10):
    versus(chain_star, 1000, 1000)

[out] :

func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6494 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6603 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6367 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6350 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6296 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6399 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6341 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6381 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6343 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6309 sec

chain_from_iterable, m=1000, n=1000

for _ in range(10):
    versus(chain_from_iterable, 1000, 1000)

[out] :

func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6416 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6315 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6535 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6334 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6327 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6471 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6426 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6287 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6353 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6297 sec

chain_star, m=10000, n=1000

func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.2659 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.2966 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.2953 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.3141 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.2802 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.2799 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.2848 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.3299 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.2730 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.3052 sec

chain_from_iterable, m=10000, n=1000

func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.3129 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.3064 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.3071 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.2660 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.2837 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.2877 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.2756 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.2939 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.2715 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.2877 sec

chain_star, m=100000, n=1000

func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 62.7874 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 63.3744 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 62.5584 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 63.3745 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 62.7982 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 63.4054 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 62.6769 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 62.6476 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 63.7397 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 62.8980 sec

chain_from_iterable, m=100000, n=1000

for _ in range(10):
    versus(chain_from_iterable, 100000, 1000)

[out] :

func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.7227 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.7717 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.7159 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.7569 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.7906 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.6211 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.7294 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.8260 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.8356 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.9738 sec

chain_star, m=500000, n=1000

for _ in range(3):
    versus(chain_from_iterable, 500000, 1000)

[out] :

func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 500000, 1000), {}] took: 314.5671 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 500000, 1000), {}] took: 313.9270 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 500000, 1000), {}] took: 313.8992 sec

chain_from_iterable, m=500000, n=1000

for _ in range(3):
    versus(chain_from_iterable, 500000, 1000)

[out] :

func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 500000, 1000), {}] took: 313.8301 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 500000, 1000), {}] took: 313.8104 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 500000, 1000), {}] took: 313.9440 sec

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X