Durchläuft man einen Generator in einer Schleife über denselben Generator, der in Python sicher ist?

8

Soweit ich weiß, ist eine for x in a_generator: foo(x) -Schleife in Python ungefähr gleichwertig:

%Vor%

Das deutet auf Folgendes hin:

%Vor%

würde zwei Dinge tun:

  1. Keine Ausnahmen, segfault oder ähnliches auslösen
  2. Iterate über y , bis x erreicht ist, wobei should_inner_loop(x) truthy zurückgibt, und dann im inneren for , bis stop_inner_loop(thing) true zurückgibt. Dann setzt die äußere Schleife fort, wobei die innere unterbrochen wird .

Von meinen zugegebenermaßen nicht sehr guten Tests scheint es zu funktionieren wie oben. Ich konnte jedoch nichts in der Spezifikation finden, was garantiert, dass dieses Verhalten bei Interpretern konstant ist. Gibt es irgendwo, was sagt oder impliziert, dass ich sicher sein kann, dass es immer so sein wird? Kann es zu Fehlern kommen oder auf andere Weise funktionieren? (d. h. etwas anderes als das oben beschriebene tun

N.B. Der obige Code entspricht meiner eigenen Erfahrung. Ich weiß nicht, ob es tatsächlich richtig ist. Deshalb frage ich.

    
Nic Hartley 17.05.2016, 18:47
quelle

3 Antworten

6

TL; DR: Es ist sicher mit CPython (aber ich konnte keine Spezifikation davon finden), obwohl es nicht tun kann, was Sie tun möchten.

Lassen Sie uns zuerst über Ihre erste Annahme sprechen, die Äquivalenz.

Eine for-Schleife ruft tatsächlich das erste iter() für das Objekt auf und führt dann next() für das Ergebnis aus, bis es ein StopIteration erhält.

Hier ist der relevante Bytecode (eine Low-Level-Form von Python, die vom Interpreter selbst verwendet wird):

%Vor%

GET_ITER ruft iter(y) auf (welches selbst y.__iter__() aufruft) und schiebt sein Ergebnis auf den Stack (denke an eine Menge lokaler unbenannter Variablen), dann tritt die Schleife bei FOR_ITER ein, die% aufruft co_de% (die selbst next(<iterator>) aufruft), führt dann den Code innerhalb der Schleife aus, und die <iterator>.__next__() bewirkt, dass die Ausführung wieder zu JUMP_ABSOLUTE zurückkehrt.

Nun zur Sicherheit:

Hier sind die Methoden eines Generators: Ссылка Wie Sie in Zeile 617 sehen können, ist die Implementierung von FOR_ITER __iter__() , dessen Implementierung Sie hier finden. PyObject_SelfIter gibt einfach das Objekt (dh den Generator) selbst zurück.

Wenn Sie also die beiden Schleifen verschachteln, durchlaufen beide den gleichen Iterator. Und wie du schon sagtest, rufen sie einfach PyObject_SelfIter auf, also ist es sicher.

Aber Vorsicht: Die innere Schleife verbraucht Gegenstände, die nicht von der äußeren Schleife verbraucht werden. Selbst wenn Sie das möchten, ist es möglicherweise nicht gut lesbar.

Wenn Sie das nicht möchten, sollten Sie next() in Betracht ziehen. Dadurch wird die Ausgabe eines Iterators zwischengespeichert, sodass Sie die Ausgabe zweimal (oder häufiger) durchlaufen können. Dies ist nur effizient, wenn die Tee-Iteratoren im Ausgabestrom nahe beieinander bleiben. Wenn ein Tee-Iterator vollständig erschöpft ist, bevor der andere verwendet wird, ist es besser, einfach itertools.tee() auf dem Iterator aufzurufen, um daraus eine Liste zu erstellen.

    
Valentin Lorentz 17.05.2016, 19:17
quelle
3

Nein, es ist nicht sicher (wie in, wir werden nicht das Ergebnis bekommen, das wir erwartet hätten).

Bedenken Sie Folgendes:

%Vor%

Natürlich werden wir 0 bis 19 gedruckt bekommen.

Nun fügen wir ein bisschen Code hinzu:

%Vor%

Das einzige, was gedruckt wird, ist 0 . Zu der Zeit, zu der wir zu der zweiten Iteration der äußeren Schleife kommen, wird der Generator bereits durch die innere Schleife erschöpft sein.

Wir können das auch tun:

%Vor%

Wenn es sicher wäre, würden wir erwarten, dass 20 bis 20 gedruckt werden, aber wir bekommen es tatsächlich nur einmal gedruckt, aus dem gleichen Grund, den ich oben erwähnt habe.

    
DeepSpace 17.05.2016 19:04
quelle
2

Es ist nicht wirklich eine Antwort auf Ihre Frage, aber ich würde empfehlen, dies nicht zu tun, weil der Code nicht lesbar ist. Ich habe eine Weile gebraucht, um zu sehen, dass du y zweimal benutzt hast, obwohl das der ganze Punkt deiner Frage ist. Lassen Sie einen zukünftigen Leser nicht durcheinander bringen. Wenn ich eine verschachtelte Schleife sehe, erwarte ich nicht, was Sie getan haben, und mein Gehirn hat Schwierigkeiten, es zu sehen.

Ich würde es so machen:

%Vor%     
Alex Hall 17.05.2016 19:15
quelle

Tags und Links