2012-07-22 19 views
14

Tenía un poco de curiosidad si podía hacer más trabajo en una función después de devolver un resultado. Básicamente, estoy creando un sitio usando el marco piramidal (que simplemente está codificando en Python) después de procesar las entradas. Devuelvo variables para representar la página, pero a veces quiero hacer más trabajo luego de renderizar la página.¿Hay alguna manera de hacer más trabajo después de una declaración de devolución?

Por ejemplo, vienes a mi sitio y actualizas tu perfil y todo lo que te importa es que es exitoso, así que dejo un mensaje diciendo '¡éxito!' pero después de eso, quiero tomar su actualización y actualizar mis registros de actividad de lo que hace, actualizar las corrientes de actividad de sus amigos, etc. En este momento estoy haciendo todo eso antes de devolver el estado del resultado que a usted le importa, pero yo ' Me da curiosidad si puedo hacerlo después, para que los usuarios obtengan sus respuestas más rápido.

He hecho multiprocesamiento antes y, en el peor de los casos, podría simplemente pasar un hilo para hacer este trabajo, pero si hubiera una forma de hacer el trabajo después de una declaración de devolución, entonces sería más simple.

ejemplo:

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

No estoy familiarizado con el enhebrado de python, pero la mayoría de los modelos de subprocesamiento, la creación de un hilo es tan simple como llamar a una función. La complejidad proviene de asegurarse de que el trabajo que se realiza en el hilo se sincronice de forma adecuada con las cosas que están sucediendo de forma asincrónica con el hilo. Me parece que la complejidad existiría en el mismo grado con lo que quiera que ocurra en el paso posterior al retorno '#do maintainence processing now..'. Si no fuera necesaria la sincronización allí, entonces ninguno debería ser necesario en un hilo. Pero, lo contrario también es cierto. –

Respuesta

9

No, por desgracia, una vez que se pulsa el comunicado return, regrese de la función/método (ya sea con o sin un valor de retorno).

Desde el docs for return:

de regreso sale de la llamada a la función actual con la lista de expresiones (o ninguno) como valor de retorno.

Es posible que desee ver en las funciones del generador y la declaración yield, esta es una manera de devolver un valor desde una función y continuar con el procesamiento y la preparación de otro valor a ser devuelto cuando la función es llamada la próxima vez .

+0

interesante, no pensé en el rendimiento ... pensé que se usaba para devolver un valor a la función llamándolo no como una respuesta. Básicamente estoy devolviendo una respuesta http en forma de diccionario. No puedo cambiar la forma en que HTTPS acepta devoluciones, pero haré algunas excavaciones para ver si puedo reemplazar mi comando con yield. Interesante idea, gracias. – Lostsoul

+0

No creo que el rendimiento sea útil aquí. Una función que utiliza el rendimiento genera una secuencia de valores tal como son requeridos por la iteración sobre el resultado de la llamada. La persona que llama debería ser modificada para esperar una secuencia de la función, para tratar el primer elemento como una respuesta, y luego solicitar más valores para permitir que el código se ejecute después del primer rendimiento. – Ben

+0

@Ben Pero es evidente que la persona que llama tiene que indicar cuándo se debe ejecutar el mantenimiento, ya que si todo está bien, podemos ejecutarlo antes de volver. Lo que en retorno significa que necesitamos dos llamadas de todos modos y eso es justo lo que necesita la coroneta. – Voo

13

¿Por qué no utiliza un contextmanager? Básicamente hace exactamente lo que quieres.

Aquí está el ejemplo canónico de los documentos de Python.

from contextlib import contextmanager 

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

Así que para su función, sólo haría:

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

Y llamarlo, sólo haría:

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: Sólo estaba probando cosas fuera, y resulta que, con una declaración de rendimiento, la función todavía se ejecuta hasta el final. Aquí hay un ejemplo tonto que ilustra el punto y cuando las cosas se ejecutan.

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] 

Un ContextManager tiene la ventaja de no requerir dos next llamadas para llegar a la iteración de parada (y la sintaxis más limpia), pero si usted quiere devolver múltiples valores o algo así, también podría hacerlo de esta manera, pero se puede ver que la declaración de rendimiento poste no lo consigue realmente llama hasta que el generador plantea StopIteration en la llamada next (el bucle termina cuando se pone StopIteration)


Si por alguna razón, se requiere un mayor grado de control que @contextmanager ofrece, puede als o definir una clase con __enter__ y __exit__ métodos:

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, buena edición allí. ese es un buen punto que '@ contextmanager' es solo azúcar sintáctico para una clase más compleja. –

+0

Creo que 'q' debe ser' [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 'no' [0, 1, 2, 3, 4] 'en su ejemplo –

+0

@ AlexandrPriymak, sí, lo cambié cuando corría en mi máquina, lo arreglaré. –

6

Aún se podía hacer un trabajo después de la vuelta si vuelve a intentarlo-bloque, el fin del bloque habría todavía ser ejecutados, por ejemplo:

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

Citando the docs:

Cuando el retorno pasa el control de una sentencia try con un fin cláusula, que finalmente la cláusula se ejecuta antes de realmente dejar la función .

+5

Pero finalmente se ejecutará * antes * de que la persona que llama reciba el valor devuelto. Esto no ayuda a que la persona que llama reciba una respuesta y notifique al usuario, pero * luego * ejecuta más código. – Ben

+2

Dependiendo del caso de uso, eso podría estar bien. Podría ser una técnica de control de flujo conveniente para envolver los contenidos de su función, y tener una variedad de declaraciones de retorno diferentes, todas las cuales dejarían caer el flujo directamente al bloque finally para realizar el mantenimiento. – andyortlieb

3

No, una devolución devuelve el valor a la persona que llama y se detiene.

Si la persona que llama (s) también están bajo su control (no forma parte de la estructura de pirámide), usted podría cambiar profile_updates a tener el siguiente aspecto:

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

Y luego codificar la persona que llama a esperar un par de (response, task), en lugar de solo una respuesta. Puede hacer algo inmediatamente con la pieza response (comunicarlo al usuario), luego llamar al task() para manejar el postprocesamiento.

Esto permite profile_update determinar qué código debe ejecutarse después (y mantener esos detalles ocultos y encapsulados desde el nivel superior), pero permite que el nivel superior determine el flujo de comunicación de una respuesta al usuario y luego ejecutar el postprocesamiento en segundo plano.

+0

+1 ¡Gran idea, voy a hacer algunas pruebas pero una forma muy muy interesante de pensar sobre este problema! – Lostsoul

+0

Huh. No tenía idea de que Python podría hacer esto. ¡Gracias por su amabilidad! –

-1

Es posible engañar con la estructura try-except-finally. Ejemplo:

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 

Tenga en cuenta, que la declaración finally se ejecutará incluso si una excepción fue capturado.

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.. 

Aquí usamos la funcionalidad de subprocesamiento de python.

Primero llamamos a la función de actualización (también puede usar esta función en el hilo si es necesario y si la respuesta no depende de esta función y si necesita dar la respuesta inmediatamente).

Luego, creamos un hilo que completará la función de mantenimiento-función y se detendrá después de terminarlo. Pero la respuesta no se retrasará hasta que se termine esa función.

es decir, se devolverá el resultado "funcionó" y luego también el hilo mantiene el funcionamiento de la función función de mantenimiento si es un proceso de bit.

Cuestiones relacionadas