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

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

No, unfortunately, once you hit the return statement, you return from the function/method (either with or without a return value).

From the docs for return:

return leaves the current function call with the expression list (or None) as return value.

You may want to look into generator functions and the yield statement, this is a way to return a value from a function and continue processing and preparing another value to be returned when the function is called the next time.


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.

Tags:

Python