Kann eine Python-Funktion einen Generator nehmen und Generatoren auf Teilmengen ihrer erzeugten Ausgabe zurückgeben?
Frage
Lassen Sie uns sagen, dass ich eine Generatorfunktion wie folgt aussehen:
import random
def big_gen():
i = 0
group = 'a'
while group != 'd':
i += 1
yield (group, i)
if random.random() < 0.20:
group = chr(ord(group) + 1)
Beispiel Ausgabe könnte so aussehen: ( 'A', 1), ( 'a', 2), ( 'a', 3), ( 'a', 4), ( 'a', 5), ( 'a', 6), (‘ a‘, 7), ( 'a', 8), ( 'b', 9), ( 'C', 10), ( 'C', 11), ( 'C', 12), (c ' , 13)
Das möchte ich in drei Gruppen brechen: Gruppe A, Gruppe B und Gruppe C Und ich würde für jede Gruppe einen Generator mögen. Dann würde ich den Generator und den Gruppenbuchstaben in eine Unterfunktion übergeben. Ein Beispiel für die Unterfunktion:
def printer(group_letter, generator):
print "These numbers are in group %s:" % group_letter
for num in generator:
print "\t%s" % num
Die gewünschte Ausgabe wäre:
These numbers are in group a:
1
2
3
4
5
6
7
8
These numbers are in group b:
9
These numbers are in group c:
10
11
12
13
Wie kann ich dies tun, ohne Änderung big_gen () oder Drucker (), und vermeiden Sie die gesamte Gruppe im Speicher auf einmal zu speichern? (Im wirklichen Leben ist die Gruppen große )
Lösung
Klar, das tut, was Sie wollen:
import itertools
import operator
def main():
for let, gen in itertools.groupby(big_gen(), key=operator.itemgetter(0)):
secgen = itertools.imap(operator.itemgetter(1), gen)
printer(let, secgen)
groupby
macht den Großteil der Arbeit hier - die key=
es nur sagt, was Feld Gruppe.
Der resultierende Generator nur in einem imap
gewickelt werden muss, weil Sie Ihre printer
Unterschrift angegeben haben einen Iterator über Nummer zu nehmen, während von Natur groupby
Iteratoren über die gleichen Elemente gibt es als seine Eingabe wird - hier, 2-Elemente Tupel mit einem Buchstaben und eine Zahl -. aber das ist nicht wirklich alles, was relevant für Ihre Frage Titel
Die Antwort auf diesen Titel ist, dass, ja, kann eine Python-Funktion sehr gut, den Job Sie wollen - itertools.groupby
in der Tat tut genau das. Ich empfehle das Studium des itertools Modul vorsichtig, es ist ein sehr nützliches Werkzeug (und liefert großartige Leistung als gut).
Andere Tipps
Sie haben ein kleines Problem hier. Sie würden die Funktion Drucker () wie für jede Gruppe einen Generator zu nehmen, aber in Wirklichkeit haben Sie den gleichen Generator alle Gruppen ergibt. Sie haben zwei Möglichkeiten, wie ich es sehe:
1) Veränderung big_gen () Generatoren zu erhalten:
import random
def big_gen():
i = 0
group = 'a'
while group != 'd':
def gen():
i += 1
yield i
if random.random() < 0.20:
group = chr(ord(group) + 1)
yield group, gen
from itertools import imap
imap(lambda a: printer(*a), big_gen())
2) Ändern der Drucker () zu halten Zustand und beachten, wenn die Gruppe (Beibehaltung Ihrer ursprünglichen big_gen () Funktion):
def printer(generator):
group = None
for grp, num in generator:
if grp != group:
print "These numbers are in group %s:" % grp
group = grp
print "\t%s" % num