Quick summary
Summarize this blog with AI
return ends the current function call and supplies a value to its caller. It does not print the value, exit every caller, or merely leave the nearest loop. A function that reaches its end without returning a value returns None. These examples use Python 3.12.14 and can run in order.
Return a result instead of only printing it
def add(left, right):
return left + right
def show_total(left, right):
print(left + right)
print("returned:", add(2, 3))
result = show_total(2, 3)
print("show_total returned:", result)
Expected output:
returned: 5
5
show_total returned: None
Printing is an observable side effect; returning makes a value available for composition and testing. An interactive console may display a return value automatically, while a script usually requires print to display it. Do not confuse that interface behavior with what the function returns.
Use early return to implement a first-match contract
def first_even(values):
for value in values:
if value % 2 == 0:
return value
return None
print(first_even([1, 4, 2]))
print(first_even([1, 3]))
print(first_even([]))
print(first_even([0, 2]))
Expected output:
4
None
None
0
This helper expects integers and returns the first even input, not their sum. Zero is a valid returned value, so test result is None for absence instead of if not result. A break would leave only the loop and continue executing the function. A return exits the function immediately. If None could also be a valid found value, choose a distinct sentinel or a result type that separates absence from the value.
Make all branches obey one contract
def describe(values):
if not values:
return None
return min(values), max(values)
print(describe([4, 2, 9]))
print(describe([]))
low, high = describe([4, 2, 9])
print(low, high)
Expected output:
(2, 9)
None
2 9
The comma constructs one tuple; Python is still returning one object. The documented empty case is None, so the caller must check it before unpacking an uncertain input. This example accepts a sequence supporting truth testing and repeatable iteration, not an arbitrary one-shot iterator. Type hints can document an intended return type but do not enforce it at runtime. Raising an exception is appropriate for an invalid request; returning None is appropriate only when absence is a normal documented outcome.
A generator return ends iteration
def samples():
yield 10
yield 20
return "complete"
iterator = samples()
print(next(iterator), next(iterator))
try:
next(iterator)
except StopIteration as stopped:
print("completion value:", stopped.value)
print(list(samples()))
Expected output:
10 20
completion value: complete
[10, 20]
Because the function contains yield, calling it creates a generator. The return value becomes the completion exception’s value; it is not an extra yielded item. Ordinary for loops and list construction consume yielded values and do not append the completion value. yield from can capture a delegated generator’s completion value when that is the intended protocol.
Let cleanup run without replacing the result
A finally block and context-manager exit still run while a function returns. Avoid return inside finally: it can replace an earlier return value and suppress an exception, hiding failure. Return only the result the contract promises, and leave cleanup to a with block or a finally block that does not mask outcomes. Code following an unconditional return in the same block is unreachable. A return outside a function is a compile-time error; see the focused syntax repair.