defer.execute effettivamente eseguire la funzione in modo blocco, nello stesso thread e si è corretta in quanto defer.execute(f, args, kwargs)
fa lo stesso come defer.succeed(f(*args, **kwargs))
tranne che defer.execute
restituirà un callback che ha avuto l'errback licenziato se la funzione f genera un'eccezione. Nel frattempo, nell'esempio defer.succession, se la funzione ha generato un'eccezione, si propagherebbe verso l'esterno, cosa che potrebbe non essere desiderata.
Per una maggiore comprensione, mi limiterò a incollo la fonte di defer.execute qui:
def execute(callable, *args, **kw):
"""Create a deferred from a callable and arguments.
Call the given function with the given arguments. Return a deferred which
has been fired with its callback as the result of that invocation or its
errback with a Failure for the exception thrown.
"""
try:
result = callable(*args, **kw)
except:
return fail()
else:
return succeed(result)
In altre parole, defer.execute
è solo una scorciatoia per prendere il risultato di una funzione di blocco come differito che è possibile quindi aggiungere callback/errback a. I callback verranno attivati con la semantica di concatenazione normale. Sembra un po 'pazzo, ma i Deferred possono "sparare" prima di aggiungere i callback e i callback saranno ancora chiamati.
Quindi, per rispondere alla tua domanda, perché è questo utile? Bene, defer.execute
è utile sia per il testing/mocking che per la semplice integrazione di un'async asincrono con codice sincrono.
Anche utile è defer.maybeDeferred
che chiama la funzione e quindi se la funzione restituisce già un rinvio, semplicemente lo restituisce, altrimenti funziona come defer.execute
. Questo è utile quando scrivi un'API che si aspetta un callable che quando viene chiamato ti dà un rinvio e vuoi essere in grado di accettare anche le normali funzioni di blocco.
Ad esempio, supponiamo che tu abbia un'applicazione che recupera le pagine e ne fa le cose. E, per qualche ragione, è necessario eseguire questo in modo sincrono per un caso d'uso specifico, come in uno script crontab single-shot, o in risposta a una richiesta in un'applicazione WSGI, mantenendo comunque la stessa base di codice. Se il codice si presentava così, potrebbe essere fatto:
from twisted.internet import defer
from twisted.web.client import getPage
def process_feed(url, getter=getPage):
d = defer.maybeDeferred(getter, url)
d.addCallback(_process_feed)
def _process_feed(result):
pass # do something with result here
Per eseguire questo in un contesto sincrono, senza il reattore, si può solo passare una funzione getter alternativa, in questo modo:
from urllib2 import urlopen
def synchronous_getter(url):
resp = urlopen(url)
result = resp.read()
resp.close()
return result
+ 1 per spiegare defer.maybeDeferred –