Python Return Statement: How return Works in Functions

The Python return statement ends a function and sends a value back to the line that called it. Write return followed by the value; if you leave the value out, or skip return completely, the caller gets None:

def add(a, b):
    return a + b

total = add(3, 4)   # total is 7

I ran each snippet below on Python 3.12.5 from a Windows Command Prompt and pasted the printed results as they came out.

In this tutorial, we’ll cover return vs print(), implicit None, early returns, loops, returning strings, lists, dicts and functions, and yield.

How does the return statement work in Python?

When Python reaches return, it evaluates the expression, stops the function and hands the result back. Lines after it never run:

def sales_tax(amount, rate=0.0825):
    tax = round(amount * rate, 2)
    return tax
    print("this line never runs")

tax = sales_tax(59.99)
print(f"Tax: ${tax:.2f}")
print(f"Total: ${59.99 + tax:.2f}")
print(100 * 0.07)               # why the round() matters

Output:

Tax: $4.95
Total: $64.94
7.000000000000001

The 100 * 0.07 line shows why I round money before returning it: floats can’t store 0.07 exactly, so the raw result is slightly off.

return only works inside a def block; at module level it’s a SyntaxError: return outside function. If you’re new to the def line itself, start with how to define a function in Python.

Return vs print in Python

print() shows a value on the screen, while return gives it to your code. A function that only prints still returns None:

def total_print(prices):
    print(round(sum(prices), 2))

def total_return(prices):
    return round(sum(prices), 2)

cart = [19.99, 5.25, 12.00]
a = total_print(cart)
b = total_return(cart)
print("a =", a)
print("b =", b)
print(f"With shipping: ${b + 4.99:.2f}")
try:
    a + 4.99
except TypeError as err:
    print("TypeError:", err)

Output:

37.24
a = None
b = 37.24
With shipping: $42.23
TypeError: unsupported operand type(s) for +: 'NoneType' and 'float'
Command Prompt output comparing a Python function that prints a total with one that returns it
Both functions show the same total, but only the returning one gives a value you can add shipping to; the printing one leaves None.

Use print() to look at values and return whenever other code needs the result.

Functions that purposely return nothing are void functions in Python.

Why does my Python function return None?

A function returns None in three cases: it has no return, it uses a bare return, or the path it took never reached a return. The last one is the sneaky one:

def check_even(n):
    if n % 2 == 0:
        return True                   # no return for odd numbers

def save_order(order_id):
    print(f"Order {order_id} saved")
    return                            # bare return

print(check_even(4))
print(check_even(5))
print(save_order(1042))

Output:

True
None
Order 1042 saved
None
Command Prompt output of Python functions that return None implicitly and after a bare return
The odd number and the bare return both come back as None, while the even number returns True.

check_even(5) skips the if block and falls off the end. Make sure every path returns something, or return the condition directly with return n % 2 == 0.

Early return with if statements

A function can have several return statements, and the first one that runs wins. That lets you reject bad input at the top instead of nesting the main logic deep inside if blocks:

def discount(price):
    if price < 0:
        return None
    if price > 100:
        return round(price * 0.10, 2)
    return round(price * 0.05, 2)

for price in (150.00, 60.00, -5.00):
    print(price, "->", discount(price))

Output:

150.0 -> 15.0
60.0 -> 3.0
-5.0 -> None

A negative price stops at the first check. These top-of-function checks are called guard clauses, and they keep the normal path flat.

Raising an exception or calling sys.exit() are other ways out; how to exit a function in Python compares them.

Using return inside a for loop

A return inside a loop stops the loop and the function at once, which is exactly what you want when you’re searching for the first match. It’s also the source of a classic bug:

orders = [("A-1001", "Megan Bowen", 120.50),
          ("A-1002", "Alex Wilber", 89.99),
          ("A-1003", "Adele Vance", 240.00)]

def first_big_buggy(limit):
    for order_id, _, amount in orders:
        if amount > limit:
            return order_id
        else:
            return None               # gives up after the first order

def first_big(limit):
    for order_id, _, amount in orders:
        if amount > limit:
            return order_id           # leaves the loop and the function
    return None                       # only after checking every order

print("buggy:", first_big_buggy(200))
print("fixed:", first_big(200))
print("none over $500:", first_big(500))

Output:

buggy: None
fixed: A-1003
none over $500: None
Command Prompt output of a Python return statement inside a for loop finding the first matching order
The buggy version gives up before reaching the third order, the fixed one finds A-1003, and a limit no order passes gives None.

The buggy version returns in its else branch, so it quits after the first order. Put the “not found” return after the loop.

If you’d like a refresher on how the loop itself walks through a list, see the Python for loop.

Returning multiple values from a Python function

Separate the values with commas, as in return count, total, and Python packs them into one tuple that the caller unpacks with count, total = summary(orders). For named tuples, dataclasses and dicts, see returning multiple values from a function in Python.

Return a string, list or dictionary in Python

return works with any object: a string built with an f-string or join(), a list or a dict. Triple-quoted multiline strings return the same way:

def greet(name):
    return f"Hello, {name}!"

def receipt(items):
    return "\n".join(f"{name:<8} ${price:>6.2f}" for name, price in items)

def big_spenders(orders, limit):
    return [name for name, amount in orders if amount > limit]

def totals(orders):
    return {name: amount for name, amount in orders}

orders = [("Megan", 120.50), ("Alex", 89.99), ("Adele", 240.00)]
print(greet("Megan"))
print(receipt([("Coffee", 4.75), ("Bagel", 3.25)]))
print(big_spenders(orders, 100))
print(totals(orders))

Output:

Hello, Megan!
Coffee   $  4.75
Bagel    $  3.25
['Megan', 'Adele']
{'Megan': 120.5, 'Alex': 89.99, 'Adele': 240.0}
Command Prompt output of Python functions that return a string, a list and a dictionary
Each function’s return value prints as a greeting, a two-line receipt, a list of names and a dictionary.

Returning a new list or dict is safer than changing one that was passed in. Pick one return type per function so callers don’t have to guess.

Defaults pair nicely with these helpers, for example a limit=100 parameter; see Python functions with optional arguments.

Return a function from a function (closures)

Functions are objects too, so a function can return another one. The inner function remembers the variables of the outer one, which is called a closure:

def make_tax(rate):
    def apply(amount):
        return round(amount * (1 + rate), 2)
    return apply                      # the function itself, not a call

wa_total = make_tax(0.065)
tx_total = make_tax(0.0625)
print(wa_total(100.00), tx_total(100.00))

Output:

106.5 106.25

make_tax() returns apply without parentheses, so you get the function itself. Each one keeps its own rate, so Washington and Texas give different totals.

The inner function sees rate because of Python variable scope. A one-line return lambda amount: ... works too; see lambda functions in Python.

Return vs yield in Python

return sends one value and ends the function. yield turns the function into a generator that hands out values one at a time and pauses in between:

def squares_list(n):
    return [i * i for i in range(n)]

def squares_gen(n):
    for i in range(n):
        yield i * i                   # pauses until the next value is asked for

print(squares_list(5))
gen = squares_gen(5)
print(next(gen), next(gen), list(gen))

Output:

[0, 1, 4, 9, 16]
0 1 [4, 9, 16]

The generator hands out 0 and 1 on demand, and list() collects the rest. Use yield for long sequences you don’t want in memory at once.

Two more pages on getting data out of a function pair well with this one:

Frequently asked questions

What does return do in Python?

It ends the function and sends a value back to the caller.

Is return the same as print in Python?

No. print() only shows text and returns None; return gives the value to your code.

What does a Python function return without a return statement?

None, the same as a bare return or a path that never reaches one.

Can a Python function have more than one return statement?

Yes. The first one that runs ends the function.

How do I return every match from a loop instead of the first one?

Append each match to a list and return the list after the loop, or yield each match.

Where is the return statement documented?

See the return statement in the official Python language reference.