2017-01-29 4 views
3

Was ist der Pythonic-Weg, um einen Generator zu erzeugen, der auch aggregierte Ergebnisse liefert? In Meta-Code, so etwas wie diese (aber nicht für real, wie meine Python-Version nicht unterstützt Ausbeute Mischen und zurück):Python-Funktion, die sowohl Generator- als auch Aggregat-Ergebnisse liefert

def produce(): 
    total = 0 
    for item in find_all(): 
     total += 1 
     yield item 

    return total 

Wie ich es sehe, könnte ich:

  1. nicht machen produce() ein Generator, aber übergeben Sie ihm eine Rückruffunktion, um alle item anzurufen.
  2. Mit jedem yield, auch yield die aggregierten Ergebnisse bis jetzt. Ich würde lieber nicht die Zwischenergebnisse mit jeder Ausbeute berechnen, nur wenn Sie fertig sind.
  3. Senden Sie eine dict als Argument an produce(), die mit den aggregierten Ergebnissen ausgefüllt wird.
  4. Verwenden Sie eine globale, um aggregierte Ergebnisse zu speichern.

Alle von ihnen scheinen nicht sehr attraktiv ...

NB. total ist ein einfaches Beispiel, mein tatsächlicher Code erfordert komplexe Aggregationen. Und ich brauche Zwischenergebnisse bevor produce() fertig ist, also ein Generator.

+1

also insgesamt nur das Äquivalent der Länge des iterable zurückgegeben von 'find_all'? Dafür können Sie einfach 'enumerate' verwenden. –

Antwort

3

Vielleicht sollten Sie keinen Generator verwenden, sondern einen Iterator.

def findall(): # no idea what your "find_all" does so I use this instead. :-) 
    yield 1 
    yield 2 
    yield 3 

class Produce(object): 
    def __init__(self, iterable): 
     self._it = iterable 
     self.total = 0 

    def __iter__(self): 
     return self 

    def __next__(self): 
     self.total += 1 
     return next(self._it) 

    next = __next__ # only necessary for python2 compatibility 

Vielleicht besser sehen zu können dies mit einem Beispiel:

>>> it = Produce(findall()) 
>>> it.total 
0 
>>> next(it) 
1 
>>> next(it) 
2 
>>> it.total 
2 
0

Fehle ich etwas? Warum nicht:

def produce(): 
    total = 0 
    for item in find_all(): 
     total += 1 
     yield item 

    yield total 
+0

Danke, daran habe ich nicht gedacht! Es könnte funktionieren, aber dann müsste der Aufrufer herausfinden, ob es ein "Objekt" -Objekt oder ein "Gesamt" -Objekt erhalten hat. Hmm. – Willem

1

Sie enumerate Sachen zählen können, zum Beispiel

i=0 
for i,v in enumerate(range(10), 1): 
    print(v) 
print("total",i) 

(den Startwert des enumerate bemerken)

für komplexere Zeug, können Sie das gleiche Prinzip verwenden, um ein Generato zu produzieren r, die beide Werte ergeben und einen in der Iteration ignorieren und später verwenden.

andere Alternative ist, ein änderbares Objekt übergibt, zum Beispiel

def produce(mem): 
    t=0 
    for x in range(10): 
     t+=1 
     yield x 
    mem.append(t) 

aggregate=[] 
for x in produce(aggregate): 
    print(x) 
print("total",aggregate[0]) 

in jedem Fall das Ergebnis ist dasselbe für dieses Beispiel

0 
1 
2 
3 
4 
5 
6 
7 
8 
9 
total 10 
Verwandte Themen