Hättest du mein Post gelesen wüsstest du die Antwort.LanX hat geschrieben:Klar für Streams braucht man Iteratoren, aber werden Iteratoren in Python zwangsläufig mit yield konstruiert???
Beispiele für Realworld Use Cases für Generatoren
-
- Python-Forum Veteran
- Beiträge: 16025
- Registriert: Freitag 20. Juni 2003, 16:30
- Kontaktdaten:
Darii hat doch gezeigt wie das ohne ``yield`` gehen würde…LanX hat geschrieben:Klar für Streams braucht man Iteratoren, aber werden Iteratoren in Python zwangsläufig mit yield konstruiert???
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Jein. Du kannst auch eine Klasse schreiben die __iter__ und next implementiert und den State in Attributen speichert, ist aber wesentlich mehr Code und bei weitem nicht so elegant insbesondere dann wenn du auch noch send und throw haben willst.LanX hat geschrieben:Klar für Streams braucht man Iteratoren, aber werden Iteratoren in Python zwangsläufig mit yield konstruiert???
Ich habe deinen Post gelesen und das Prinzip der rhetorischen Frage ist dir bekannt?Darii hat geschrieben:Hättest du mein Post gelesen wüsstest du die Antwort.LanX hat geschrieben:Klar für Streams braucht man Iteratoren, aber werden Iteratoren in Python zwangsläufig mit yield konstruiert???
(Vorsicht: die letzte Frage ist rekursiv
LanX ist der Meinung, dass die offensichtliche Antwort "Nein" lautet. Ich bin aber, wie Xynon1, der Meinung, dass die Antwort eben nicht offensichtlich ist und die Frage daher auch keine rhetorische ist. BTT: noch ein use-case für yield: wenn man z.B. mittels try-except ne Ausnahme abfangen muss, kann man ja auch keine GE verwenden.
Das die Implementierung so mehr Aufwand ist liegt aber vermutlich am Scope von Python. Denn man kann eben nicht sowas schreiben:LanX hat geschrieben: Schon klar mir geht's aber um das Idiom. Mit Statusvariablen im Closure kann ich eine Funktion auch bereits ohne "yield" zur Continuation machen, um dieses Ziel zu erreichen.
Es ist halt mehr Aufwand.
Ein eigenständiges Idiom hingegen rechtfertigt sich über Häufigkeit der Anwendungsfälle.
Code: Alles auswählen
def generator(x, n):
y = 0
def f():
y += 1
return (x**y) % n
return f
Ohne yield koennte man das z.B. mit mit einem zusaetzlichen dictionary als namespace implementieren:
Code: Alles auswählen
def generator(x, n):
namespace = {'y': 0}
def f():
namespace['y'] += 1
return (x**namespace['y']) % n
return f
Mit yield vereinfacht sich der Code dann etwas, da man den zusaetzlichen namespace nicht braucht:
Code: Alles auswählen
def generator(x, n):
y = 0
while True:
y += 1
yield (x**y) % n
Generatoren mit yield haben aber den Nachteil das man (afaik) next() nicht mit Parametern aufrufen kann, den accumulator generator kann man also mit yield nicht implementieren?
Code: Alles auswählen
def accgen(n):
namespace = {'n': n}
def f(i):
namespace['n'] += i
return namespace['n']
return f
@crs: `next()` nicht, aber `send()`. Allerdings muss man mindestens einmal vorher `next()` aufgerufen haben, damit der Code einmal bis zum `yield` abgearbeitet wurde:
Testlauf:
Code: Alles auswählen
def f(n):
while True:
x = yield n
n += x
Code: Alles auswählen
In [58]: a = f(0)
In [59]: a.next()
Out[59]: 0
In [60]: a.send(42)
Out[60]: 42
In [61]: a.send(23)
Out[61]: 65
In [62]: a.send(1)
Out[62]: 66
Meinst du das jetzt ernst? Aber immerhin bin ich nicht der einzige der dich nicht verstanden hat.LanX hat geschrieben:Ich habe deinen Post gelesen und das Prinzip der rhetorischen Frage ist dir bekannt?
`next()` wird sowieso nie mit Parametern aufgerufen.crs hat geschrieben:Generatoren mit yield haben aber den Nachteil das man (afaik) next() nicht mit Parametern aufrufen kann, den accumulator generator kann man also mit yield nicht implementieren?
Eher sehr theoretischer Natur, aber du könntest Generatoren, mittels ``yield``, auch für kooperatives Multitasking verwenden. Wenn ich mich richtig entsinne, gab es im Cookbook dazu sogar ein Beispiel.
Sebastian
Sebastian
Das Leben ist wie ein Tennisball.
Hmm, ich weiß dass Continuations ein Sonderfall von Co-Routinen sind, ich vermute das läuft darauf hinaus.EyDu hat geschrieben:Eher sehr theoretischer Natur, aber du könntest Generatoren, mittels ``yield``, auch für kooperatives Multitasking verwenden. Wenn ich mich richtig entsinne, gab es im Cookbook dazu sogar ein Beispiel.
Sebastian
Ich merke auch gerade, dass ich etwas zu wenig von Python verstehe, viele Pattern die ich kennen sind nur mit ziemlich viel Aufwand abbildbar.crs hat geschrieben: Das die Implementierung so mehr Aufwand ist liegt aber vermutlich am Scope von Python. Denn man kann eben nicht sowas schreiben:Code: Alles auswählen
def generator(x, n): y = 0 def f(): y += 1 return (x**y) % n return f
Wenn man einen Closure als Iterator verwenden will braucht man Unverhältnis viel mehr Code, als wenn man ein Iterator-Objekt nutzt, was aber eine bewusste Designentscheidung von GvR ist, die wir jetzt nicht auszuflamen brauchen.
folgender Perlcode der den deinen um ein Abbruchkriterium erweitert lässt sich IMHO auch fast 1 zu 1 genauso in LISP oder JS abbilden:
Code: Alles auswählen
sub generator {
my ($x, $n) = @_;
my $y = 0;
return
sub {
my ($max) = @_;
$y++;
return
$y <= $max
? ($x ** $y ) % $n
: ()
};
}
my $iter = generator(2,5);
while ( my ($val) = $iter->(9) ) {
print $val,"\t";
}
# Ausgabe: 4 3 1 2 4 3 1 2
Sprich man kommt gar nicht darum herum eine Iterator-Objekt zu erzeugen. (oder hab ich hier was verpasst?)
In Perl gibts zwar diverse Iterator-Klassen die man nutzen könnte die Funktionale Lösung ist nur viel schneller als Methodenaufrufe der Art "$iter_obj->next()".
Ich vermute mal die strikte OOP in Python erlaubt hier Optimierungen in den Ausführungszeiten der impliziten Methodenaufrufe in For-in.
EDIT: Initialisierungsfehler korrigiert und wieder zurückgenommen um zum folgecode kompatibel zu bleiben.
Zuletzt geändert von LanX am Samstag 2. April 2011, 19:11, insgesamt 2-mal geändert.
@LanX: ``for`` will grundsätzlich nach dem ``in`` ein "iterable" haben. Wenn Du eine Funktion hast die bei jedem Aufruf einen Wert erzeugt und das Ende durch einen "sentinel"-Wert anzeigt, kannst Du so ein "iterable" mit der `iter()`-Funktion erzeugen. Dein Code so nahe wie möglich am Original in Python:
Code: Alles auswählen
from functools import partial
def generator(x, n):
y = [0]
def f(max_):
y[0] += 1
return (x**y[0]) % n if y[0] <= max_ else None
return f
def main():
iter_func = generator(2, 5)
for val in iter(partial(iter_func, 9), None):
print val,
if __name__ == '__main__':
main()
Ich habe nicht den ganzen Thread gelesen sondern nur das Wort "Continuation". Mit yield (oder sonst irgendwie in Python kann man keine Continuations bauen. Eine Continuation ist eine Funktion, die an einem bestimmten Programmpunkt für den Rest des Programms steht. Wenn ich also "3+4" habe und gerade eben die "3" (von links nach rechts) ausgewertet habe, wäre die Continuation an dieser Stelle die Funktion f(x) = x + 4. In diesem trivialen Beispiel ziemlich unsinnig, aber allgemein interessant, denn weil ich, wenn ich an beliebiger Stelle im Programmablauf mit eine Funktion geben lassen kann, die für den Rest des Programms steht, kann ich mir dieses quasi aufheben und doch etwas anderes machen. Dies wird für Coroutinen benutzt. Diese lassen sich mit Continuations implementieren - aber nicht anders herum. Eine Coroutine ist nur einmal zu durchlaufen, eine Continuation als "echte" Funktion kann beliebig häufig wiederholt werden. Eine noch eingeschränktere Form von Continuation wäre die Escape-Continuation, bei Python try/except und raise genannt. Continuations lassen sich wiederum benutzen, um diese Form ausergewöhnlichen Kontrollflusses zu implementieren - aber nicht anders herum.
Bei Smalltalk oder Dylan (Scheme sowieso und AFAIK auch Common Lisp) sind Exceptions übrigens wiederholbar, d.h. man kann dem System sagen, man möge es an der Stelle, wo abgebrochen wurde, um dann den Exception-Handler auszuführen, doch bitte weitermachen, d.h. in diesem Fall enthalten die Exception-Objekte die Continuation für diese Programmstelle. Total praktisch, weil das nämlich den Debugger erlaubt, in dem man auf einen Fehler laufen kann, diesen dann korrigiert und das Programm weiterlaufen lässt.
Stefan
Bei Smalltalk oder Dylan (Scheme sowieso und AFAIK auch Common Lisp) sind Exceptions übrigens wiederholbar, d.h. man kann dem System sagen, man möge es an der Stelle, wo abgebrochen wurde, um dann den Exception-Handler auszuführen, doch bitte weitermachen, d.h. in diesem Fall enthalten die Exception-Objekte die Continuation für diese Programmstelle. Total praktisch, weil das nämlich den Debugger erlaubt, in dem man auf einen Fehler laufen kann, diesen dann korrigiert und das Programm weiterlaufen lässt.
Stefan
Hisma hat geschrieben:Ich habe nicht den ganzen Thread gelesen sondern nur das Wort "Continuation". Mit yield (oder sonst irgendwie in Python kann man keine Continuations bauen. Eine Continuation ist eine Funktion, die an einem bestimmten Programmpunkt für den Rest des Programms steht. ...
Dies wird für Coroutinen benutzt. Diese lassen sich mit Continuations implementieren - aber nicht anders herum. Eine Coroutine ist nur einmal zu durchlaufen, eine Continuation als "echte" Funktion kann beliebig häufig wiederholt werden. Eine noch eingeschränktere Form von Continuation wäre die Escape-Continuation, bei Python try/except und raise genannt. Continuations lassen sich wiederum benutzen, um diese Form ausergewöhnlichen Kontrollflusses zu implementieren - aber nicht anders herum.
Ich habe tatsächlich Continuation und Coroutine falsch rum in der Erinnerung gehabt, sorry.
Danke!
Allerdings werden Python Generatoren in der tat oft mit Coroutinen identifiziert:
Z.B. hier
Python's generator functions are almost coroutines -- but not quite -- in that they allow pausing execution to produce a value, but do not provide for values or exceptions to be passed in when execution resumes.
oder
In Python 2.5 wurde die Syntax des yield-Schlüsselworts erweitert, um die Übergabe von Parametern in die andere Richtung zu ermöglichen.Damit können Koroutinen vollständig in Python implementiert werden
ich habe jetzt ein bisschen recherchiert und es ist wie üblich mühsam sich durch unterschiedliche Umsetzungen diverser Sprachen inkl. verbundenem Begriffswandel und unverifizierter Einträge bei WP zu kämpfen.
Ich besorge mir die Tage den Knuth und schau mir mal an was er dazu geschrieben hat, das Buch ist IMHO alt genug um nahe an den Primärquellen zu sein und anerkannt genug um als Referenz zu dienen.
Tschau
Rolf