Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to do more work after a return statement?

Tags:

python

I was a bit curious if I could do more work in a function after returning a result. Basically I'm making a site using the pyramid framework(which is simply coding in python) after I process the inputs I return variables to render the page but sometimes I want to do more work after I render the page.

For example, you come to my site and update your profile and all you care about is that its successful so I output a message saying 'success!' but after that done I want to take your update and update my activity logs of what your doing, update your friends activity streams, etc.. Right now I'm doing all that before I return the result status that you care about but I'm curious if I can do it after so users get their responses faster.

I have done multi-processing before and worst case I might just fork a thread to do this work but if there was a way to do work after a return statement then that would be simpler.

example:

def profile_update(inputs):   #take updates and update the database    return "it worked"   #do maintenance processing now. 
like image 794
Lostsoul Avatar asked Jul 22 '12 23:07

Lostsoul


People also ask

Can you continue a function after return?

No. But yes. No line of the function will be executed after the return statement. However, the return statement also marks the end of the function and therefor the end of the scope.

How do you continue after a return in Python?

The continue statement in Python returns the control to the beginning of the while loop. The continue statement rejects all the remaining statements in the current iteration of the loop and moves the control back to the top of the loop. The continue statement can be used in both while and for loops.

Can you have code after return statement?

Code after return statement gets executed even if function does not specify a return type.

What happens to a line of code written after return statement?

No, code is never executed after a return statement is reached. If, however, condition1 is false, then the return statement isn't reached, so execution proceeds normally. This is exactly the way Java behaves, too.


2 Answers

You could still do some work after return if you return from a try-block, the finally-block would still be executed, e.g.:

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

Expected Output:

Yay! I still got executed, even though my function has already returned! 100 

Quoting the docs:

When return passes control out of a try statement with a finally clause, that finally clause is executed before really leaving the function.

like image 169
Giel Avatar answered Sep 19 '22 21:09

Giel


Why don't you use a contextmanager? It basically does exactly what you want.

Here's the canonical example from the Python docs.

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

So for your function, you'd just do:

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

And to call it, you'd just do:

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: I was just testing things out, and it turns out that, with a yield statement, the function still executes to the end. Here's a dumb example that illustrates the point and when things get executed.

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 has the advantage of not requiring two next calls to get to the stop iteration (and cleaner syntax), but if you wanted to return multiple values or something, you could also do it this way, but you can see that the post yield statement doesn't actually get called until the generator raises StopIteration on the next call (the for loop ends when it gets StopIteration)


If for some reason, you require a higher degree of control than @contextmanager offers, you can also define a class with __enter__ and __exit__ methods:

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 
like image 22
Jeff Tratner Avatar answered Sep 20 '22 21:09

Jeff Tratner