2012-07-22 2 views
14

Ich war ein bisschen neugierig, ob ich mehr Arbeit in einer Funktion machen könnte, nachdem ich ein Ergebnis zurückgegeben habe. Im Grunde mache ich eine Website mit dem Pyramiden-Framework (das einfach in Python codiert), nachdem ich die Eingaben verarbeitet habe. Ich gebe Variablen zurück, um die Seite zu rendern, aber manchmal möchte ich nach dem Rendern der Seite mehr arbeiten.Gibt es eine Möglichkeit, nach einer Rücksendeanweisung mehr Arbeit zu leisten?

Zum Beispiel kommen Sie auf meine Website und aktualisieren Sie Ihr Profil und alles, was Sie interessieren, ist, dass es erfolgreich ist, so dass ich eine Nachricht mit dem Titel "Erfolg!" aber danach möchte ich dein Update nehmen und meine Aktivitäts-Logs aktualisieren, was du tust, deine Aktivitäts-Streams für deine Freunde aktualisierst usw. Momentan mache ich das alles, bevor ich den Ergebnisstatus zurückgebe, der dir wichtig ist, aber ich ' Ich bin neugierig, ob ich es nachher kann, damit die Benutzer ihre Antworten schneller bekommen.

Ich habe multi-processing vor und schlimmsten Fall gemacht ich könnte nur einen Thread fork diese Arbeit tun, aber wenn es eine Möglichkeit gab, nach einer return-Anweisung Arbeit zu tun, dann wäre das einfacher.

Beispiel:

def profile_update(inputs): 
    #take updates and update the database 
    return "it worked" 
    #do maintainence processing now.. 
+1

Ich bin nicht vertraut mit Python-Threading, aber die meisten Threading-Modelle, starten einen Thread ist so einfach wie das Aufrufen einer Funktion. Die Komplexität entsteht dadurch, dass sichergestellt wird, dass die Arbeit, die im Thread erledigt wird, mit den Dingen synchronisiert wird, die asynchron zum Thread ablaufen. Es scheint mir, dass Komplexität in gleichem Maße mit dem existiert, was Sie im '#do maintainence process now..' Post-Return-Schritt erreichen möchten. Sollte dort keine Synchronisation notwendig sein, sollte in einem Thread keines notwendig sein. Aber das Gegenteil ist auch wahr. –

Antwort

9

Nein, leider, sobald Sie die return Aussage treffen, kehren Sie von der Funktion/Methode (entweder mit oder ohne Rückgabewert).

Vom docs for return:

Rückkehr verlässt den aktuellen Funktionsaufruf mit der Ausdrucksliste (oder keine) als Rückgabewert.

Möglicherweise möchten Sie in Generator-Funktionen suchen und die yield Aussage ist dies ein Weg, um einen Wert aus einer Funktion zurückzukehren und weiterverarbeiten und einen anderen Wert Vorbereitung zurückgegeben werden, wenn die Funktion das nächste Mal aufgerufen wird, .

+0

interessant, dachte nicht an die Ausbeute..Ich dachte, es wurde verwendet, um einen Wert an die Funktion zurückgeben es nicht als eine Antwort zu nennen. Im Grunde gebe ich eine HTTP Antwort in Form eines Wörterbuches zurück. Ich kann nicht ändern, wie HTTPS Rückgaben annimmt, aber ich werde etwas graben und sehen, ob ich meinen Befehl durch Ertrag ersetzen kann. Interessante Idee, danke. – Lostsoul

+0

Ich glaube nicht, dass Rendite hier helfen würde. Eine Funktion, die yield verwendet, generiert eine Folge von Werten, wie sie für die Iteration über das Ergebnis des Aufrufs erforderlich sind. Der Aufrufer müsste geändert werden, um eine Sequenz von der Funktion zu erwarten, das erste Element als Antwort behandeln und dann weitere Werte anfordern, damit der Code nach dem ersten Ertrag ausgeführt werden kann. – Ben

+0

@Ben Aber natürlich muss der Anrufer angeben, wann die Wartungsarbeiten ausgeführt werden sollen, denn wenn alles in Ordnung ist, können wir es einfach vor der Rückkehr ausführen. Was im Gegenzug bedeutet, dass wir sowieso zwei Anrufe brauchen, und das ist genau das, was die Koroutine braucht. – Voo

13

Warum verwenden Sie keine contextmanager? Es macht im Grunde genau das, was Sie wollen.

Hier ist das kanonische Beispiel aus den Python-Dokumenten.

from contextlib import contextmanager 

@contextmanager 
def tag(name): 
    print "<%s>" % name 
    yield 
    print "</%s>" % name 

Also für Ihre Funktion, würden Sie gerade tun:

@contextmanager 
def profile_update(inputs): 
    #take updates and update the database 
    yield "it worked" 
    #do maintainence processing now.. 

Und es zu nennen, würden Sie gerade tun:

with profile_update(inputs) as result: #pre-yield and yield here 
    # do whatever while in scope 
# as you move out of scope of with statement, post-yield is executed 

EDIT: Ich habe nur die Dinge zu testen out, und es stellt sich heraus, dass die Funktion mit einer yield-Anweisung immer noch bis zum Ende ausgeführt wird. Hier ist ein dummes Beispiel, das den Punkt darstellt und wann die Dinge ausgeführt werden.

def some_generator(lst): 
    for elem in lst: 
     yield elem 
    lst[0] = "I WAS CHANGED POST-YIELD!!!!" 

>>> q = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
>>> gen = some_generator(q) 
>>> for e in gen: 
... print e, q 

0 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
1 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
2 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
3 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
4 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
5 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
6 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
7 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
8 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
9 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 

print q 
['I WAS CHANGED POST YIELD!!!', 1, 2, 3, 4, 5, 6, 7, 8, 9] 

A contextmanager hat den Vorteil, nicht erfordern zwei next Anrufe zum Anschlag Iteration (und saubere Syntax) zu bekommen, aber wenn Sie mehrere Werte oder etwas zurückgeben wollen, könnten Sie es auch auf diese Weise tun, aber können Sie den Generator wirft StopIteration auf dem next Aufruf aufgerufen wird der Beitrag yield-Anweisung nicht wirklich sehen, dass bis (die for-Schleife endet, wenn es StopIteration bekommt)


aus irgendeinem Grund Wenn Sie einen höheren Grad erfordern der Kontrolle als @contextmanager bietet, können Sie auch o definiert eine Klasse mit __enter__ und __exit__ Methoden:

class MyContextClass(object): 
    # ... 

    def __enter__(self): 
     # do some preprocessing 
     return some_object 

    def __exit__(self, exc_type, exc_value, traceback): 
     # do some post processing 
     # possibly do some processing of exceptions raised within the block 
     if exc_type == MyCustomErrorType: 
      return True #don't propagate the error 
+0

@JoelCornett, schön dort bearbeiten. Das ist ein guter Punkt, dass '@ contextmanager' nur syntaktischer Zucker für eine komplexere Klasse ist. –

+0

Ich denke 'q' sollte sein' [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 'nicht' [0, 1, 2, 3, 4] 'in Ihrem Beispiel –

+0

@ AlexandrPriymak: Ja, ich habe es geändert, als ich auf meiner Maschine lief, ich repariere es. –

6

Sie noch etwas Arbeit nach der Rückkehr tun könnte, wenn man von einem Try-Block zurückzukehren, die schließlich Block würde noch ausgeführt werden, zB:

def fun(): 
    try: 
     return 
    finally: 
     print "Yay! I still got executed, even though my function has already returned!" 

Zitiert the docs:

Wenn Rückkehr geht die Steuerung aus einer try-Anweisung mit einem schließlich clause, dass die finally-Klausel ausgeführt wird, bevor die Funktion wirklich verlassen wird.

+5

Aber das würde schließlich noch laufen * bevor * der Aufrufer den zurückgegebenen Wert erhält. Dies hilft Ihnen nicht, dem Anrufer zu erlauben, eine Antwort zu erhalten und den Benutzer zu benachrichtigen, aber * dann * mehr Code auszuführen. – Ben

+2

Je nach Anwendungsfall könnte das in Ordnung sein. Es könnte eine bequeme Ablaufsteuerungstechnik sein, um den Inhalt Ihrer Funktion in try zu verpacken, und eine Vielzahl von verschiedenen Rückgabeanweisungen zu haben, die alle den Fluss direkt an den finally-Block absetzen würden, um die Wartung durchzuführen. – andyortlieb

3

Nein, eine Rückgabe gibt den Wert an den Aufrufer zurück und stoppt.

Wenn der Anrufer (n) sind auch unter Ihrer Kontrolle (nicht Teil der Pyramide Rahmen), könnten Sie profile_updates wie folgt aussehen ändern:

def profile_update(inputs): 
    #take updates and update the database 
    def post_processing_task(): 
     #do maintainence processing now.. 
    return ("it worked", post_processing_task) 

Und dann codieren den Anrufer um ein Paar zu erwarten von (response, task), anstatt nur eine Antwort. Es kann etwas sofort mit dem response Teil (kommunizieren Sie es mit dem Benutzer), dann task() aufrufen, um die Nachbearbeitung zu behandeln.

Dies ermöglicht profile_update zu bestimmen, welcher Code danach ausgeführt werden muss (und diese Details von der höheren Ebene verdeckt und verkapselt zu halten), ermöglicht es jedoch der höheren Ebene, den Kommunikationsfluss einer Antwort an den Benutzer zu bestimmen und dann die Ausführung auszuführen Nachbearbeitung im Hintergrund.

+0

+1 Tolle Idee, ich werde einige Tests machen, aber sehr, sehr interessante Art, über dieses Problem nachzudenken! – Lostsoul

+0

Huh. Ich hatte keine Ahnung, dass Python das tun könnte. Dankesehr! –

-1

Es ist möglich, mit der Struktur try-except-finally zu betrügen. Beispiel:

def function(): 
    try: 
    #do some stuff here 
    return x 
    except Exception: 
    #do something when an error occures 
    finally: 
    #do here whatever you wanna do after return 

Beachten Sie, dass die finally Anweisung selbst ausgeführt werden, wenn eine Ausnahme abgefangen wurde.

3
import threading 

def profile_update(inputs): 

    # call function to take updates and update the database 
    update_function(inputs) 

    # call the maintainence_function here 
    t = threading.Thread(target=maintainence_function, args=[input1, input2]) 
    # setDaemon=False to stop the thread after complete 
    t.setDaemon(False) 
    # starting the thread 
    t.start() 

    # return response/anything-else you want to return 
    return "it worked" 



def update_function(inputs): 
    # updating the database process here 

def maintainence_function(input1, input2): 
    #do maintainence processing now.. 

Hier verwenden wir die Threading-Funktionalität von Python.

Zuerst rufen wir die Update-Funktion auf (Sie können diese Funktion bei Bedarf auch im Thread verwenden und wenn die Antwort nicht von dieser Funktion abhängt und Sie die Antwort sofort geben müssen).

Dann haben wir einen Thread erstellt, der die maintainance_function-Funktion vervollständigt und nach dem Beenden stoppt. Aber die Antwort wird nicht verzögert, bis diese Funktion beendet ist.

dh: return "es hat funktioniert" wird zurückgegeben und dann auch der Thread behält die Funktion der Funktion Maintenance_function wenn ts ein bisschen Prozess.

Verwandte Themen