2017-05-08 3 views
6

Ich habe einen Generator definiert, die Protokolleinträge aus Elasticsearch ergibt:Schleife durch Generator zwei Elemente gleichzeitig

def gen(): 
    .... 
    for hit in results: 
     yield hit 

Wie kann ich Schleife durch zwei Elemente zur gleichen Zeit? Etwas in den Zeilen:

for one, two in gen(): 
    ... 

von zwei Elementen ich dies bedeuten: A, B, B, C, ..., Y, Z (für eine erzeugte Liste A, B, ..., Y, Z).

Antwort

6

Ihre aktualisierte Frage zu beantworten, verwenden itertools.tee zur Zusammenarbeit nstruct einen zweiten Iterator, führe den zweiten Iterator einmal weiter und verwerfe das Ergebnis, dann wiederhole die beiden Iteratoren paarweise unter Verwendung von zip.

>>> from itertools import tee 
>>> it = iter('abc') 
>>> it1, it2 = tee(it) 
>>> 
>>> next(it2, None) 
'a' 
>>> for first, second in zip(it1, it2): 
...  first, second 
... 
('a', 'b') 
('b', 'c') 

Danke. Dies ist der sauberste, "pythonischste" Weg, es zu tun? Ich denke, die Lösung für dieses einfache Problem ist komplex.

Ich glaube nicht, dass es eine sauberere Lösung gibt. In der Tat ist es das pairwise Rezept aus dem itertools docs:

def pairwise(iterable): 
    "s -> (s0,s1), (s1,s2), (s2, s3), ..." 
    a, b = tee(iterable) 
    next(b, None) 
    return izip(a, b) 
+0

Vielen Dank. Dies ist der sauberste, "pythonischste" Weg, es zu tun? Ich denke, die Lösung für dieses einfache Problem ist komplex. – linkyndy

+0

Danke, dass du auf das Itertoolrezept hingewiesen hast! – linkyndy

+1

@AndreiHorak kein Problem, stellen Sie sicher, "izip" wie im Rezept zu verwenden, wenn Sie Python 2 verwenden, besonders wenn Ihre Iteratoren/Generatoren eine unendliche Menge an Objekten ergeben könnten. – timgeb

6

Diese Antwort geht davon aus, dass Sie nicht überlappende Paare möchten. Sie können dies über zip() tun, weil der Iterator verbraucht wird:

for one, two in zip(gen, gen): 
    # do something 

Beispiel:

>>> gen = (x for x in range(5)) 
>>> for one, two in zip(gen, gen): print(one,two) 
... 
0 1 
2 3 

Hinweis, wie timgeb kommentiert, sollten Sie itertools.zip_longest verwenden, wenn Sie eine ungerade Anzahl von Elementen haben, und Sie wollen, dass die letzte mit einem Füllungswert, zum Beispiel:

>>> gen = (x for x in range(5)) 
>>> for one, two in zip_longest(gen, gen): print(one, two) 
... 
0 1 
2 3 
4 None 
+0

Wird dies sicherzustellen, die gleiche Reihenfolge für die Elemente und Wiederverwendung des zuvor geladenen Artikels? – linkyndy

+2

Beachten Sie, dass dies nicht zum letzten Element führt, wenn eine ungleichmäßige Anzahl von Objekten vom Generator geliefert wird. In diesem Fall erhalten Sie mit 'itertools.izip_longest'' (last_element, None) '(oder einen beliebigen Füllwert). – timgeb

+0

@AndreiHorak was meinst du "Wiederverwendung des zuvor geladenen Elements"? – timgeb

0

einmal gelöst ich das ähnliche Problem mit folgenden:

def gen(): 
    results = [1, 2, 3, 4, 5] 
    for i, v in enumerate(results): 
     # if i < len() 
     if (i + 1) < len(results): 
      yield (v, results[i + 1]) 
     else: 
      yield v 


output = gen() 

for each in output: 
    print(each) 

Ausgang wird sein:

(1, 2) 
(2, 3) 
(3, 4) 
(4, 5) 
5 
+0

Da der Generator durchsuchte Elasticsearch-Ergebnisse durchläuft, finde ich es nicht optimal, alles in den Speicher zu laden, um die Elemente zu zweit zu gruppieren. – linkyndy

Verwandte Themen