Generatoren/Iterable und deren...

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
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

...irgendwas ^^ Siehe unten.

Dieser Thread letzter Post: http://www.python-forum.de/topic-7955.h ... hlight=24h

Da die Diskussion nicht in den oberen Thread passt möchte ich sie gerne hier weiterführen.
BlackJack hat geschrieben:Da sind 3 Iteratoren. `count()` liefert einen, `xrange()` ist "iterable", liefert also indirekt auch einen und dann `izip()`. Der von `izip()` wird von der Schleife benutzt.
Naja, 'count()', etc. ist auch iterabel. Also wird insgesamt über 4 iteriert. Oder anders: man kann über die alle Generator-Funktionen iterieren wie über 'xrange()' weil der __iter__ implementiert ist (auch wenn nur im lowlevel) bei einigen). Also hat es die annähernd gleiche Laufzeit.

In dem Beispiel wäre...

Code: Alles auswählen

for coordinates in izip(count(1), xrange(7, 50, 3)): 
...
...exakt äquivalent zu...

Code: Alles auswählen

for coordinates in izip(xrange(1,counts), xrange(7,counts, 3)): 
...weil die Laufzeit der beiden zu iterierenden Objekte gleich ist!

Der unendliche Generator 'count()' wird durch 'izip()' terminiert dessen Terminierungszeitpunkt von 'xrange()' bestimmt wird. Und zwar wenn 'xrange()' eine ''raises StopIteration'' bei ereichen von 49 auslöst, was interen abgefangen wird von Python.

So weit OK ;) Die goldene Frage lautet nun: Was macht 'izip()' genau?

Reicht er einen Iterator (Kombination aus zurückgegebene Element von 'count(1)' und 'xrange(50)' = '(yield count(1).next(), xrange(50).next()) ') an 'for' weiter, der dann '.next()' von 'izip()' aufruft, was dann zu folgen hat das ein tuple von 'izip()' zurückgegeben wird ( '(1, 7)...' ) __oder__ wird erstmal 'count()' und 'xrange()' zu ende iteriert und deren Ergebnisse temporär in eine variable von 'izip()' gespeichert, das dann bei jeder Iteration von 'izip()' eine Element der Temporären Variabel von 'izip()' yielded? Ich hoffe du kanns mir folgen.


Ich verstehe nicht genau den Verarbeitungsablauf wenn Generatoren __mehrere__ Generatoren/iterable ausführen und so weiter :? und in wie fern die Laufzeit sich erhöht.

Meiner Meinung nach müsste ein Generator der einen Generator entgegenimt, und dessen Elemente yielden will, doch über den entgegenommenen generator iterieren. Dadurch ergibt wider eine zusätliche schleife. Weist du was ich meine und worauf ich hinaus will?

ich hoffe damit wird es klarer:

Code: Alles auswählen

def f1(iterable):
    for iter in iterable:
        yield iter

for i in  f1(xrange(50)):
    print i
f1 ist ein Generator der iterable entgegennimmt und dessen elemente yielded. Damit das gehen kann muss über den Entgegengenommenem Generator iteriert werden. Dadurch ergibt sich eine zusätzliche Schleife.
Was ich mich jetzt frage ist, ob die "Laufzeit" von 'f1(xrange(50)' die gleiche ist wie von 'xrange(50)' oder gar von 'f1(f1(f1(xrange(50)))):'
Meiner Meinung nach müsste die sich doch erhöhen weil über 4 statt 1 Objekt iteriert wird oder? :?

Kann mir da einer sagen was genau Sache ist?
BlackJack

XtraNine hat geschrieben:
BlackJack hat geschrieben:Da sind 3 Iteratoren. `count()` liefert einen, `xrange()` ist "iterable", liefert also indirekt auch einen und dann `izip()`. Der von `izip()` wird von der Schleife benutzt.
Naja, 'count()', etc. ist auch iterabel. Also wird insgesamt über 4 iteriert. Oder anders: man kann über die alle Generator-Funktionen iterieren wie über 'xrange()' weil der __iter__ implementiert ist.
Nein es sind 3 Iteratoren. Wo bekommst Du den vierten her!? Und das mit `xrange()` habe ich nur hervorgehoben, weil die Funktion ein "iterable" zurückgibt, das selbst noch kein Iterator ist, während `izip()` und `count()` direkt einen Iterator zurückgeben. Das ist aber letztendlich nicht so wichtig.
Die goldene Frage lautet nun: Was macht 'izip()' genau?

Reicht er einen Iterator (Kombination aus zurückgegebene Element von 'count(1)' und 'xrange(50)' = '(yield count(1).next(), xrange(50).next()) ') an 'for' weiter, der dann '.next()' von 'izip()' aufruft, was dann zu folgen hat das ein tuple von 'izip()' zurückgegeben wird ( '(1, 7)...' ) __oder__ wird erstmal 'count()' und 'xrange()' zu ende iteriert und deren Ergebnisse temporär in eine variable von 'izip()' gespeichert, das dann bei jeder Iteration von 'izip()' eine Element der Temporären Variabel von 'izip()' yielded? Ich hoffe du kanns mir folgen.
Mehr oder weniger. `izip()` fordert nur die Elemente an, die gerade gebraucht werden und speichert nichts weiter. Sonst wäre es schliesslich nur ein anderer Name für `zip()`. Insbesondere wenn man `izip()` zwei "unendliche" Iteratoren geben würde, wäre das ziemlich unpraktisch. Eine simple Implementierung sieht so aus:

Code: Alles auswählen

def myzip(iterable_a, iterable_b):
    iterator_a = iter(iterable_a)
    iterator_b = iter(iterable_b)
    while True:
        yield (iterator_a.next(), iterator_b.next())
Ich verstehe nicht genau den Verarbeitungsablauf wenn Generatoren __mehrere__ Generatoren/iterable ausführen und so weiter :? und in wie fern die Laufzeit sich erhöht.

Meiner Meinung nach müsste ein Generator der einen Generator entgegenimt, und dessen Elemente yielden will, doch über den entgegenommenen generator iterieren. Dadurch ergibt wider eine zusätliche schleife. Weist du was ich meine und worauf ich hinaus will?
Jup das gibt zusätzliche Schleifen. Die im Falle von den `itertools` aber in C geschrieben sind, und damit schneller sind als Schleifen in Python-Bytecode. Und die zusätzliche Laufzeit ist in O-Notation ausgedrückt konstant und nicht etwa exponential, da die Schleifen sozusagen parallel ausgeführt werden.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Ah nun hab ich's verstanden :D Danke.

lg
Antworten