11 Stimmen

Wie kombiniere ich callLater und addCallback?

Dies ist so kaputt, ich hoffe, du bist barmherzig mit mir:

reactor.callLater(0, myFunction, parameter1).addCallback(reactor.stop)
reactor.run()

myFunction gibt ein Deferred zurück.

Ich hoffe, es ist klar, was ich tun möchte:

  • Sobald der Reaktor läuft, möchte ich myFunction aufrufen. Deshalb verwende ich 0 als Verzögerungsparameter. Gibt es keinen anderen Weg außer callLater? Es sieht komisch aus, ihm eine Verzögerung von 0 zu übergeben.
  • Ich möchte den Reaktor stoppen, sobald myFunction die Aufgabe abgeschlossen hat.

Die Probleme, die ich bisher habe:

  • AttributeError: Die Instanz DelayedCall hat kein Attribut 'addCallback'. Gut so! Wie füge ich dann eine Rückruffunktion in die Rückrufkette ein, die von myFunction gestartet wurde?
  • Ausnahmefehler: stop() erwartet genau 1 Argument (2 gegeben).

Um das zweite Problem zu lösen, musste ich eine spezielle Funktion definieren:

def stopReactor(result):
    gd.log.info( 'Ergebnis: %s' % result)
    gd.log.info( 'Reaktor wird sofort gestoppt' )
    reactor.stop()

Und den Code ändern zu:

reactor.callLater(0, myFunction, parameter1).addCallback(stopReactor)
reactor.run()

(funktioniert immer noch nicht wegen des callLater-Problems, aber stopReactor wird jetzt funktionieren)

Gibt es wirklich keinen anderen Weg, reactor.stop aufzurufen, außer indem man eine zusätzliche Funktion definiert?

24voto

Jean-Paul Calderone Punkte 46659

IReactorTime.callLater und Deferred werden von twisted.internet.task.deferLater gemischt.

from twisted.internet import reactor, task

d = task.deferLater(reactor, 0, myFunction, parameter1)
d.addCallback(lambda ignored: reactor.stop())
reactor.run()

0 Stimmen

Ich finde diese Option sehr lesbar. Der lambda ignoriert Teil sieht für mich magisch aus: Könnten Sie klarstellen, was es genau macht?

4 Stimmen

Callbacks auf einem Deferred werden mit einem Argument aufgerufen. reactor.stop akzeptiert keine Argumente. lambda ignored: reactor.stop() akzeptiert ein Argument, ignoriert es und ruft reactor.stop ohne Argumente auf.

4 Stimmen

Es wäre etwas konventioneller, zu schreiben: 'lambda _: reactor.stop'

1voto

Karl Knechtel Punkte 55450

Ich möchte den Reaktor stoppen, sobald myFunction die Aufgabe abgeschlossen hat.

Also, einen Wrapper erstellen, der die Arbeit von myFunction erledigt und dann den Reaktor stoppt?

def wrapper(reaktor, *args):
    myFunction(*args)
    reaktor.stop()

reaktor.callLater(0, wrapper, reaktor, ...)

0voto

ben w Punkte 2452

Du musst den Callback an das Deferred anhängen, das myFunction zurückgibt, da callLater keine Funktion zurückgibt. Etwas wie das könnte funktionieren:

reactor.callLater(0, lambda: myFunction(parameter1).addCallback(lambda _: reactor.stop())

Aber das ist nicht getestet.

Du musst eine neue Funktion schreiben (hier das lambda _: reactor.stop()), weil Callbacks für ein Deferred immer das bis dahin erreichte Ergebnis übernehmen. Wenn du feststellst, dass du Callbacks für ihre Seiteneffekte verwenden möchtest und dir die Weitergabe von Werten oft egal ist, könntest du eine kleine Hilfsfunktion definieren:

def ignoringarg(f):
    return lambda _: f()

Und dann:

reactor.callLater(0, lambda: myFunction(parameter1).addCallback(ignoringarg(reactor.stop)))

(Was wirklich praktisch wäre, wäre die Definition eines __rshift__ (und des In-Place-Analogons) für die Deferred-Klasse, damit du machen könntest: myFunction(parameter1) >> reactor.stop, wenn du das Argument verwerfen möchtest, oder myFunction(parameter1) >>= someotherfunc wenn du das Argument weitergeben möchtest. Wenn du denkst, dass der Missbrauch haskellischer Syntax "praktisch" ist, sowieso.)

0voto

yan Punkte 196

Wenn Sie einen Rückruf mit einer Aktion auslösen müssen, tun Sie es einfach (möglicherweise ist es nicht erforderlich, deferred zurückzugeben oder etwas Ähnliches). Zur Klärung der Dinge (ausschließlich Verzögerte):

from twisted.internet import reactor, defer

# Das wird unser Deferred sein, mit dem wir spielen können
# es hat Rückruf- und Fehlerauslösemethoden
d = defer.Deferred()

def my_function(x):
    print 'Funktion', x
    # muss das Deferred bei Ausführung der Funktion ausgelöst werden?
    # Lassen Sie es uns so sagen:
    d.callback(x)

# Das ist unser Rückruf, der nach Auslösen von `d` ausgeführt werden soll    
def d_callback(y):
    print 'Rückruf ', y

# binden wir jetzt diesen Rückruf, der tatsächlich von `d` ausgeführt werden soll
d.addCallback(d_callback)

# fügen wir jetzt einen weiteren Rückruf hinzu, um den Reaktor zu stoppen
# Hinweis: Lambda hilft einfach dabei, die Anzahl der Argumente festzulegen
d.addCallback(lambda daten: reactor.stop())

# Also werden wir `my_function` in 2 Sekunden aufrufen, dann wird es ausgeführt
# dann löst es `d` aus, um seine Rückrufe auszulösen
# dann löst `d` tatsächlich die gesamte Kette seiner hinzugefügten Rückrufe aus

reactor.callLater(2, my_function, 'asdf') # 'asdf' ist ein dummer Parameter

# Hier ist wie es funktioniert
print 'Los geht's!'
reactor.run()
print 'Erledigt!'

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X