9

Ich bin neu in Python und funktionaler Programmierung. Ich verwende Version 2.7.6Einen Wert aus einer Coroutine in Python ergeben, einen kA-Umwandler in einen Generator konvertieren

Ich verwende das Tornado-Framework, um asynchrone Netzwerkanforderungen zu erstellen. Von dem, was ich über die funktionale Programmierung gelernt habe, möchte ich, dass meine Daten mit Hilfe von Generatoren durch meinen Code strömen. Ich habe das meiste getan, was ich brauche, Generatoren zu verwenden und die Daten zu transformieren, während sie durch meine Funktionsaufrufe strömen.

Am Ende meines Streams möchte ich eine REST-Anforderung für einige Daten machen. Ich habe eine for-Schleife, kurz bevor ich meine Daten an Tornado übergebe, um den Pull zu initiieren und dann die http-Anfrage zu senden. Das von Tornado bereitgestellte http-Objekt nimmt eine Callback-Funktion als Option und gibt immer ein Future zurück - das eigentlich ein Tornado-Future-Objekt und nicht der offizielle Python-Future ist.

Mein Problem ist, dass, da ich jetzt Generatoren verwende, um meine Daten durch meinen Code zu ziehen, ich nicht mehr die Rückruffunktion verwenden möchte. Mein Grund dafür ist, dass nachdem ich meine Daten vom Rückruf zurückgeholt habe, meine Daten nun durch meinen Code geschoben werden und ich keine Generatoren mehr benutzen kann.

Mein Ziel ist es, eine Schnittstelle zu schaffen, die wie so erscheint:

urls = (...generated urls...) 
responses = fetch(urls) 

Wo Antworten ist ein Generator über den abgeschlossenen Urls.

Was ich versuchte - unter vielen Dingen - ist, die Ergebnisse vom Callback in einen Generator umzuwandeln. Ich habe über so etwas nachgedacht, obwohl ich weit davon entfernt bin, es für andere Themen zu implementieren, die ich bald erklären werde. Allerdings wollte ich meine Funktion holen so etwas wie folgt aussehen:

def fetch(urls): 
    def url_generator(): 
     while True: 
      val = yield 
      yield val 

    @curry 
    def handler(gen, response): 
     gen.send(response) 

    gen = url_generator() 

    for u in urls: 
     http.fetch(u, callback=handler(gen)) 

    return gen 

ich auf das Problem der Code und Syntax konzentrieren vereinfacht, aber ich dachte, diese gut funktionieren würde. Meine Strategie war es, eine Coroutine/Generator zu definieren, an die ich die Antworten dann senden werde, wenn ich sie erhalte.

Am meisten Probleme habe ich mit der Coroutine/Generator. Selbst wenn ich einen Generator auf die obige Weise definiere und folgendes mache, bekomme ich eine Endlosschleife - das ist eines meiner Hauptprobleme.

def gen(): 
    while True: 
     val = yield 
     print 'val', val 
     yield val 
     print 'after', val 
     break 

g = gen() 
g.send(None) 
g.send(10) 

for e in g: 
    print e 

Dieser druckt val 10 after 10 im Koroutine wie bei der Pause erwartet, aber die for-Schleife wird nie den Wert von 10. Es hat nichts drucken, während der Pause gibt. Wenn ich die Pause entfernen, dann bekomme ich die unendliche Schleife:

val None 
None 
after None 
None 
val None 
None 
after None 
None 
... 

Wenn ich die for-Schleife zu entfernen, dann wird die Koroutine nur drucken val 10, wie es auf der zweiten Ausbeute wartet. Ich erwarte das. Die Verwendung führt jedoch zu nichts.

In ähnlicher Weise, wenn ich die For-Schleife entfernen und durch print next(g) ersetzen, dann bekomme ich einen StopIteration-Fehler, der bedeutet, ich rief als nächstes auf einen Generator, der keine Werte mehr hatte.

Anywho, ich bin völlig verloren, während ich tiefer in Python stürze. Ich denke, das ist eine so häufige Situation in Python, dass jemand einen guten Ansatz kennt. Ich suchte nach "Callback in Generator konvertieren" und so, aber hatte nicht viel Glück.

Auf eine andere Anmerkung könnte ich möglicherweise jede Zukunft von der HTTP-Anfrage ergeben, aber ich hatte nicht viel Glück "warten" auf die Ausbeute für die Zukunft zu vervollständigen.Ich lese viel über 'yield from', aber es scheint Python 3 spezifisch zu sein und Tornado scheint noch nicht auf Python 3 zu funktionieren.

Vielen Dank für die Anzeige und danke für jede Hilfe, die Sie zur Verfügung stellen können.

+1

Ehrfürchtig Frage :). Gut geschrieben und recherchiert. – Cyphase

+1

Danke. Das ist meine erste Frage :) – Joe

+0

Tornado funktioniert mit Python 3, Ist '@ curry' dein Generator? –

Antwort

3

Tornado funktioniert auf Python 3.

Das Problem mit dem vereinfachten Code oben ist, dass dies nicht tut, was Sie erwarten:

val = yield 

Sie erwarten, dass der Generator dort anzuhalten (zu blockieren Ihre for-loop) bis eine andere Funktion g.send(value) aufruft, aber das passiert nicht. Stattdessen verhält sich der Code wie:

val = yield None 

So ist die for-Schleife None Werte erhält so schnell, wie es sie verarbeiten kann. Nachdem es jedes None empfängt, ruft es implizit g.next() auf, das dasselbe wie g.send(None) ist. So ist Ihr Code entspricht dies:

def gen(): 
    while True: 
     val = yield None 
     print 'val', val 
     yield val 
     print 'after', val 

g = gen() 
g.send(None) 
g.send(10) 

while True: 
    try: 
     e = g.send(None) 
     print e 
    except StopIteration: 
     break 

diese Version des Codes Lesen, wo die implizite Verhalten explizit gemacht werden, ich hoffe, es ist klar, warum es gerade ist None in einer Endlosschleife zu erzeugen.

Was Sie brauchen, ist eine Möglichkeit für eine Funktion zum Hinzufügen von Elementen zum Kopf einer Warteschlange, während eine andere Funktion blockiert auf Elemente warten und zieht sie aus dem Schwanz der Warteschlange, wenn sie fertig sind. Ab Tornado 4.2 haben wir genau das:

http://www.tornadoweb.org/en/stable/queues.html

Das Web-Spider Beispiel ist in der Nähe zu dem, was Sie tun wollen, ich bin sicher, dass Sie es anpassen können:

http://www.tornadoweb.org/en/stable/guide/queues.html

+0

Jesse, vielen Dank für diese Erklärung. Ich werde es als richtig markieren, sobald ich das richtige Verhalten habe arbeiten und aktualisieren Sie meine Frage mit einer funktionierenden Lösung. Es ist lustig, nachdem ich diesen Aufruf durchgeführt hatte, plante ich, meine Ergebnisse in MongoDB zu bringen ... und ich plante, Motor zu verwenden! Ich bin gestern bei Ihrer Recherche auf Ihre persönliche Seite gestoßen. Kleine Welt :) Danke nochmal für deine Hilfe. – Joe

+0

Kein Problem, froh, dass ich ein bisschen helfen konnte. Lass es mich wissen, wenn du noch etwas brauchst. –

Verwandte Themen