Kleine Frage, zu generator objekten.

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
EnTeQuAk
User
Beiträge: 986
Registriert: Freitag 21. Juli 2006, 15:03
Wohnort: Berlin
Kontaktdaten:

Montag 28. Mai 2007, 07:46

Ich habe folgendes Problem:

Ich definiere ein Objekt, das wie ein Generator arbeitet nur halt mit einigen Funktionen mehr.
zZ sieht das ganze so aus: TokenStream

Nun möchte ich eine Methode __contains__ definieren und treffe dabei auf folgendes Problem:

Ich brauche den gesamten Inhalt von dem Generator.

Wenn ich das ganze jetzt einfach als Liste speichere

Code: Alles auswählen

l = [x for x in self.generator]
wird ja auch der generator richtig abgearbeitet. Wie kann ich den generator dann wieder an seinen "ursprung" (die stelle, wo er stand, bevor die liste erstellt wurde).
Oder macht es mehr sinn, den generator zu kopieren und den neuen dann zu durchlaufen?

Ich hoffe, ich habe mich verständlich ausgedrückt.

MfG EnTeQuAk
EyDu
User
Beiträge: 4871
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Montag 28. Mai 2007, 09:16

Schau dir mal "itertools.tee" an.
EnTeQuAk
User
Beiträge: 986
Registriert: Freitag 21. Juli 2006, 15:03
Wohnort: Berlin
Kontaktdaten:

Montag 28. Mai 2007, 10:27

Also entweder habe ich etwas falsch verstanden oder ich wende es einfach nur falsch an:

Code: Alles auswählen

In [24]: def g():
   ....:     for i in xrange(10):
   ....:         yield i
   ....:         
   ....:         

In [25]: gen = g()

In [26]: gen
Out[26]: <generator object at 0xb78fe70c>

In [27]: liste = list(tee(gen, 1))

In [28]: liste
Out[28]: [<itertools.tee object at 0xb78b728c>]

In [29]: liste = list(tee(gen, 1)[0])

In [30]: liste
Out[30]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [31]: gen.next()
---------------------------------------------------------------------------
<type 'exceptions.StopIteration'>         Traceback (most recent call last)

/home/ente/<ipython console> in <module>()

<type 'exceptions.StopIteration'>:
Also wird dort doch der Generator komplett wieder abgearbeitet.
Oder muss ich `tee(gen, 2)` verwenden und später `gen` `tee(gen, 2)[1]` zuweisen und mit `tee(gen, 2)[0]` arbeiten? Wenn dem so sein soll ;) fänd ich das fein :)
(also nach dem Motto:)

Code: Alles auswählen

In [33]: gens = tee(gen, 2)

In [34]: liste = list(gens[0])

In [35]: liste
Out[35]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [36]: gen = gens[1]

In [37]: gen
Out[37]: <itertools.tee object at 0xb78b742c>
Und um das wieder in einen richtigen Generator umzuwandeln könnte ich ja dann einfach wieder
``gen = (x for x in gen)``
verwenden.

Ich denke letztere Version kommt hier für mich in Frage.

Danke, EyDu ;)

MfG EnTeQuAk

€dit
Eine andere möglichkeit währe doch einfach

Code: Alles auswählen

In [44]: gen = g()

In [45]: gen
Out[45]: <generator object at 0xb791a2cc>

In [46]: gens = ((x for x in gen), (x for x in gen))

In [47]: gens
Out[47]: (<generator object at 0xb78b7d4c>, <generator object at 0xb78b7f0c>)
zu machen oder? Das macht doch im prinziep das gleiche, was tee macht (für meinen Anwendungsfall).
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Montag 28. Mai 2007, 11:09

Also ``itertools.tee`` verwendet man so:

Code: Alles auswählen

In [10]: gen = xrange(10)
In [11]: gen
Out[11]: xrange(10)
In [13]: from itertools import tee
In [14]: gen, copy = tee(gen, 2)
In [15]: list(copy)
Out[15]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [16]: gen.next()
Out[16]: 0
In [17]: gen.next()
Out[17]: 1
Ist aber aus der Dokumentation leicht herauszulesen gewesen. Mann sollte immer mindestens zwei Generatoren erstellen, denn der Ursprungsgenerator sollte dann nicht mehr benutzt werden.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
BlackJack

Montag 28. Mai 2007, 11:19

Aus dem Rückgabewerten von `tee()` musst Du keine "richtigen" Generatoren machen. Was Du willst sind "iterables" und das liefert `itertools.tee()` natürlich. Dort einen Generatorausdruck drumherumschreiben, der im Grunde nichts tut, ist überflüssig.

Dein letztes Beispiel macht nicht was `tee()` auch macht. Du erzeugst zwei Generatorobjekte, die mal wieder nichts tun, das wäre also äquivalent zu:

Code: Alles auswählen

gen = g()
gens = (gen, gen)
Wenn Du einen Test mit ``in`` machen möchtest, solltest Du überlegen, ob Du das mit den Generatoren nicht ganz lässt und eine Liste mit Tokens erzeugst. Nichts anderes muss `tee()` im Hintergrund auch machen. Das bringt eigentlich nur etwas wenn die Generatoren, die zurückgeliefert werden nicht allzuweit "auseinander laufen" weil intern immer soviele Elemente zwischengespeichert werden müssen, wie der grösste "Abstand" zwischen zwei Iteratoren beträgt. Und ``in`` geht potentiell das ganze "iterable" durch.
EnTeQuAk
User
Beiträge: 986
Registriert: Freitag 21. Juli 2006, 15:03
Wohnort: Berlin
Kontaktdaten:

Montag 28. Mai 2007, 11:46

Wenn Du einen Test mit ``in`` machen möchtest, solltest Du überlegen, ob Du das mit den Generatoren nicht ganz lässt und eine Liste mit Tokens erzeugst
Das war wirklich bei weitem das einfachste.

Nun muss ich erstma nochma das ganze nachschauen, ob sich der TokenStream nach außen immernoch wie ein "echter" generator verhällt.

Aber das ist eine andere Sache ;)


MfG EnTeQuAk
Antworten