Python Check if function has return statement

Question:

E.g.

def f1():
    return 1

def f2():
    return None

def f3():
    print("Hello")

Functions f1() and f2() returns something but f3() not.

a = f2()
b = f3()

And here a equals b so I can’t just compare the result of functions to check if one has return or not.

Asked By: Misha

||

Answers:

A function by definition always returns something. Even if you don’t specify it, there is an implicit return None at the end of a python function.

You can check for a “return” with the inspect module.

EDIT: I just realized. This is horribly wrong because, it’ll return True if there’s a string literal in the function that has “return” in it. I suppose a robust a regex will help here.

from inspect import getsourcelines


def f(n):
    return 2 * n


def g(n):
    print(n)


def does_function_have_return(func):
    lines, _  = getsourcelines(func)
    return any("return" in line for line in lines) # might give false positives, use regex for better checks


print(does_function_have_return(f))
print(does_function_have_return(g))
Answered By: st0le

I like @st0le’s idea of inspecting the source, but you can take it a step further and parse the source into a source tree, which eliminates the possibility of false positives.

import ast
import inspect

def contains_explicit_return(f):
    return any(isinstance(node, ast.Return) for node in ast.walk(ast.parse(inspect.getsource(f))))

def f1():
    return 1

def f2():
    return None

def f3():
    print("return")

for f in (f1, f2, f3):
    print(f, contains_explicit_return(f))

Result:

<function f1 at 0x01D151E0> True
<function f2 at 0x01D15AE0> True
<function f3 at 0x0386E108> False

Of course, this only works for functions that have source code written in Python, and not all functions do. For instance, contains_explicit_return(math.sqrt) will give you a TypeError.

Furthermore, this won’t tell you anything about whether any particular execution of a function hit a return statement or not. Consider the functions:

def f():
    if random.choice((True, False)):
        return 1

def g():
    if False:
        return 1

contains_explicit_return will give True on both of these, despite f not encountering a return in half of its executions, and g not encountering a return ever.

Last, it won’t distinguish between whether the function has a return statement or a function defined inside it has a return statement:

def f():
    def g():
        return 5

In this case, contains_explicit_return(f) will return True, even though f will never return anything, because the function g inside contains an explicit return.

Answered By: Kevin

Inspired directly by @kevin’s answer. Some rearranging to reduce the long-line-itis and a fix for one of the issues indicated in his answer, namely nested functions.

There are likely better ways to do this, I just figured an easy way is to override the behavior of ast.walk to have it skip the inner functions.

import ast
import inspect

def walk_special(node):
    from collections import deque
    todo = deque([node])
    top_level_node = node
    while todo:
        node = todo.popleft()

        # Added this conditional
        if isinstance(node, ast.FunctionDef):
            if node not in ast.iter_child_nodes(top_level_node):
                continue

        todo.extend(ast.iter_child_nodes(node))
        yield node

def contains_explicit_return(f):
    source = inspect.getsource(f)
    parsed = ast.parse(source)
    for node in walk_special(parsed):
        if isinstance(node, ast.Return):
            return True
    return False

def f1():
    return 1

def f2():
    return None

def f3():
    print("return")

def f4():
    def inner_a():
        return 2

    print("return")

def f5():
    def inner_b():
        return 3

    return "test"

for f in (f1, f2, f3, f4, f5):
    print(f, contains_explicit_return(f))

edit:

I realized this might not work in the case of function definitions nested inside other nodes. I imagine this is unlikely to be a common occurrence so, presumably, not a big deal.

Example:

def f6():
    if True:
        def foo():
            return 4
    print("done")

I would not have expected contains_explicit_return(f6) == False which is correct but unexpected from my current understanding.

Answered By: T. Smith
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.