9
votes

Alternance entre les itérateurs en python

Quel est le moyen le plus efficace d'alterner de prendre des valeurs de différents itérateurs en python, de sorte que, par exemple, alternatif (xrange (1, 7, 2), xrange (2, 8, 2)) donnerait 1, 2, 3, 4, 5, 6. Je sais qu'un moyen de mettre en œuvre ce serait: xxx

mais existe-t-il une manière plus efficace ou plus propre? (Ou, mieux encore, un iTertools fonction que j'ai manqué?)


3 commentaires

Voulez-vous dire 1, 2, 7, 8, 2, 2?


Question très similaire: Stackoverflow.com / Questions / 243865 / ...


@San Devlin: Non, 1 2 3 4 5 6 est correct. docs.python.org/library/functions.html#xrange


6 Réponses :


8
votes

Qu'en est-il de Zip? Vous pouvez également essayer Izip de izip de iTertools xxx

si ce n'est pas ce que vous voulez, veuillez donner plus d'exemples dans votre post de question.


3 commentaires

Seul, Izip ne suffit pas à mes besoins. Mais mettre une chaîne autour d'izip, comme chaîne.from_iterable (izip (itérables)) fonctionne. Je suppose que c'est la chose cool sur les ittérables. Merci!


Le problème avec zip dans ce cas est qu'il évaluera immédiatement les itérateurs, vous obligeant à renverser la sémantique génératrice.


Ce qui serait particulièrement problématique envisageant que mon étui d'utilisation initial pour cela était des itérateurs qui ont généré un nombre infini de valeurs.



2
votes

Vous pouvez définir alterner code> comme ceci:

Dummy=object()

def alternate(*iters):   
    for elt in itertools.chain.from_iterable(
        itertools.izip_longest(*iters,fillvalue=Dummy)):
        if elt is not Dummy:
            yield elt


2 commentaires

Pourrait être plus propre pour définir mannequin comme dummy = objet ()


@Chris: Merci! J'ai fait le changement.



18
votes

pour une implémentation "propre", vous voulez xxx

mais peut-être que vous voulez xxx


2 commentaires

Est-ce que cela épuise le générateur avant de le transmettre à Izip puis à la chaîne?


@Dan si iters était un générateur, il l'épuiserait, au moins dans Python 2; Exécution def z (* iters): Imprimer (type (iters)); Imprimer (iters) puis z (* (i pour i in xrange (1,18))) impression (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17) .



1
votes

S'ils sont la même longueur, ITERTOOL.IFIP peut être levé comme suit:

def alternate(*iters):
    for row in itertools.izip(*iters):
       for i in row:
           yield i


0 commentaires

7
votes

voir tourbrobin code> dans le itTools Section "Recettes" . C'est une version plus générale de remplaçante.

def roundrobin(*iterables):
    "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
    # Recipe credited to George Sakkis
    pending = len(iterables)
    nexts = cycle(iter(it).__next__ for it in iterables)
    while pending:
        try:
            for next in nexts:
                yield next()
        except StopIteration:
            pending -= 1
            nexts = cycle(islice(nexts, pending))


0 commentaires

2
votes

Il y a deux problèmes avec votre tentative:

  1. Vous n'enverrez pas chaque objet dans iters avec iTER () de sorte qu'il échouera avec itérables tels que la liste ; et
  2. par passe ing sur stopiteration Votre générateur est une boucle infinie.

    Un code simple qui résout ces deux problèmes et reste facile à lire et à comprendre: xxx


0 commentaires