Seite 1 von 1
Funktionsweise von Generatoren unklar...
Verfasst: Freitag 27. November 2015, 20:58
von mutetella
Folgendes verstehe ich nicht:
Code: Alles auswählen
In [8]: g = gen('foo')
In [9]: print g.send(None)
foo
In [10]: print g.send('bar')
bar
In [11]: print g.next()
None
Weshalb wird `value` auf `None` gesetzt oder aber nicht `value`, sondern `None` zurückgegeben?
Re: Funktionsweise von Generatoren unklar...
Verfasst: Freitag 27. November 2015, 22:37
von DasIch
Nun erstmal hilft es zu schauen was genau passiert.
Code: Alles auswählen
>>> def gen(value):
... while True:
... print('pre-yield', value)
... value = yield value
... print('post-yield', value)
...
>>> g = gen('foo')
>>> g.send(None)
pre-yield foo
'foo'
>>> g.send('bar')
post-yield bar
pre-yield bar
'bar'
>>> next(g)
post-yield None
pre-yield None
`value` wird auf `None` gesetzt. Irgendwie sowas muss auch passieren weil `value` zu irgendwas gesetzt werden muss du aber nichts an den generator schickst. Das Verhalten ist so übrigens auch
dokumentiert.
Re: Funktionsweise von Generatoren unklar...
Verfasst: Samstag 28. November 2015, 07:49
von mutetella
Ok, das ist mir nun soweit klar. Nur: Wo
steht der Generator, nachdem er mit `send` oder `next` angestupst wurde?
Code: Alles auswählen
#
1 3 2
| | |
def gen(value):| | |
while True:____| |
value =|yield value|
1: g = gen(1)
2: g.send(None)
3: g.send(2)
Wo findet die Zuweisung zu `value` statt? Beziehungsweise, hat `value` nach seiner Zuweisung und der Rückgabe noch denselben Wert und wird erst dann auf `None` gesetzt, wenn der folgende Stupser keinen Wert mitbringt?
Re: Funktionsweise von Generatoren unklar...
Verfasst: Samstag 28. November 2015, 11:34
von nezzcarth
Möglicherweise verstehe ich deine Frage nicht richtig, aber meiner Meinung nach beschreibst du den Sachverhalt doch schon so, wie ich ihn auch verstanden hatte: Für mich ist 'yield' im Prinzip ein Statement zum Pausieren, das beim Eintritt in den Pausezustand etwas an den Aufrufer zurück liefern
kann (optional, aber üblich) und bei der Rückkehr aus der Pause einen Wert in die aufgerufene Generatorfunktion zurück mitbringen
kann (optional und seltener); oder anders formuliert gewissermaßen ein "Zwei-Wege-return", das erst nach außen und dann nach innen funktioniert.
(Das Pausier-Verhalten nutzt man ja auch in der zunächst etwas seltsam anmutenden Verwendung zur
Definition von Context-Managern mit einem leeren 'yield' aus. Der Teil bis einschließlich rechts von 'yield' entspricht dem '__enter__', alles links und unterhalb von 'yield' entspricht '__exit__' und in der Pause wird der eigentliche Code ausgeführt)
mutetella hat geschrieben:Wo findet die Zuweisung zu `value` statt?
Beziehungsweise, hat `value` nach seiner Zuweisung und der Rückgabe noch denselben Wert und wird erst dann auf `None` gesetzt, wenn der folgende Stupser keinen Wert mitbringt?
Graphisch gesprochen wertet der Generator das Statement rechts von 'yield' aus, gibt es an den Aufrufer zurück und pausiert dann quasi rechts vor dem "=". Nach dem "Wiederanstupsen" steht da dann im Prinzip "value = None", wenn das Anstupsen mit next() erfolgt resp. value = <x im Ausdruck 'gen.send(x)'>, wenn man 'send' verwendet. Dann wird der Code wieder bis zum Gleichheitszeichen ausgeführt. So erkläre ich mir das zumindest.
Re: Funktionsweise von Generatoren unklar...
Verfasst: Samstag 28. November 2015, 14:14
von BlackJack
@mutetella: Nach einem `send()` oder `next()` wird die Zuweisung an `value` ausgeführt. Weil erst dann steht ja fest welcher Wert dort zugewiesen werden muss, das ist vor einem `send()` oder `next()` ja unbekannt. Oder in Bytecode ausgedrückt:
Code: Alles auswählen
In [1]: def gen(value):
...: while True:
...: value = yield value
...:
In [2]: import dis
In [3]: dis.dis(gen)
2 0 SETUP_LOOP 17 (to 20)
>> 3 LOAD_GLOBAL 0 (True)
6 POP_JUMP_IF_FALSE 19
3 9 LOAD_FAST 0 (value)
12 YIELD_VALUE
13 STORE_FAST 0 (value)
16 JUMP_ABSOLUTE 3
>> 19 POP_BLOCK
>> 20 LOAD_CONST 0 (None)
23 RETURN_VALUE
Re: Funktionsweise von Generatoren unklar...
Verfasst: Sonntag 29. November 2015, 09:12
von mutetella
Vielen Dank für das Licht in meinem dunklen Kopf...
Zum Verstehen des bytecodes fehlt mir unter anderem noch eine Vorstellung, was dieser Top Of Stack eigentlich genau ist? Einmal heißt es
LOAD_GLOBAL(namei)
Loads the global named co_names[namei] onto the stack.
LOAD_FAST(var_num)
Pushes a reference to the local co_varnames[var_num] onto the stack.
was für mich so aussieht, als ob TOS sowas wie eine Liste von Konstanten, Namen und Referenzen ist, dann steht in der Doku aber wieder
YIELD_VALUE()
Pops TOS and yields it from a generator.
STORE_FAST(var_num)
Stores TOS into the local co_varnames[var_num].
was ja danach aussieht, als ob hier nicht einzelne Elemente aus dem TOS, sondern der gesamte TOS betroffen ist. Also `YIELD_VALUE()` beispielsweise gibt den gesamten TOS zurück, was ja nicht sein kann, wenn dieser TOS eine Liste, so wie in meiner Vorstellung, wäre.
Re: Funktionsweise von Generatoren unklar...
Verfasst: Sonntag 29. November 2015, 11:00
von cofi
Nunja, der Stack ist ein Stack (oder Kellerspeicher)

Und so ein Stack hat genau eine zugaengliche Position: Die Spitze (oder ToS).
Das ist also genau ein Wert der da steht. Was genau da steht, haengt ab von den umgebenden Operationen aber das ist im Grunde alles, von Referenzen ueber Werte zu Addressen.
Re: Funktionsweise von Generatoren unklar...
Verfasst: Sonntag 29. November 2015, 12:13
von snafu
@mutetella
Bitte nicht die Begriffe vermischen. "stack", welcher in deinem ersten Zitat der Doku angesprochen wird, ist die Gesamtheit aller abgelegten (wie auch immer gearteten) Objekte. Also der komplette Stapel.
TOS (Top of Stack) ist das zuletzt abgelegte Element. Dieses oberste Element ist bei Stacks das einzige Element, das man wieder herunter nehmen kann. Wenn das TOS herunter genommen wurde, dann wird anschließend das zuvor zweitoberste Element (falls vorhanden) zum obersten Element und damit zum TOS.
Bei deinem ersten Zitat wird also jeweils etwas auf dem Stack abgelegt. Bei deinem zweiten Zitat ist jeweils das oberste Element des Stacks gemeint.
Re: Funktionsweise von Generatoren unklar...
Verfasst: Montag 30. November 2015, 05:05
von mutetella
@snafu
Das hatte ich tatsächlich nicht getrennt! Jetzt wird auch ein Schuh daraus!