Skip to main content

Function Return Values: The return Statement

The return statement sends data back from a function to its caller, transforming functions from action-only routines into reusable data processors. Without return, functions only perform tasks (like printing) but can't communicate results. This guide covers capturing return values, returning multiple values with tuples, handling implicit None, and writing cleaner code with early returns.

Key Takeaways​

  • return immediately stops function execution and sends a value back to the caller
  • Any Python data type (int, string, list, dict) can be returned; functions without explicit return implicitly return None
  • Multiple values are returned as tuples and unpacked into separate variables
  • Early returns (multiple return statements) reduce nesting and improve readability for validation

What Does the Return Statement Do and Why Is It Essential?​

The return statement serves one critical purpose: it ends function execution and sends a value back. Think of it as a contract between the function and its caller—the function promises to deliver a result.

Without return, a function performs work but you can't access the outcome:

# Without return—no way to capture the result
def add_no_return(a, b):
sum_value = a + b
print(sum_value) # Prints 30 but returns None

result = add_no_return(10, 20)
print(result) # Prints: None

# With return—result is usable
def add_with_return(a, b):
return a + b

result = add_with_return(10, 20)
print(result) # Prints: 30
print(result + 5) # Prints: 35 (can use the returned value)

The return statement is what makes functions composable—you can chain them, test them, and reuse them.


Capturing and Using Function Return Values​

When a function executes return value, that value becomes the result of the function call. You capture it by assigning to a variable:

def calculate_square(number):
"""Returns the square of a number."""
return number * number

# Call the function and capture the returned value
squared = calculate_square(5)
print(squared) # 25

# Use the returned value immediately
result = calculate_square(7) + 10
print(result) # 59 (49 + 10)

Step-by-Step Execution​

  1. calculate_square(5) is called.
  2. Inside the function: number * number evaluates to 25.
  3. return 25 stops the function and sends 25 back.
  4. The value 25 is assigned to squared.
  5. squared now holds 25 and can be used anywhere.

Returning Data from Function Calls​

Functions often return data derived from their parameters:

def celsius_to_fahrenheit(celsius):
"""Converts Celsius to Fahrenheit."""
return (celsius * 9/5) + 32

temp_f = celsius_to_fahrenheit(0)
print(temp_f) # 32.0

temp_f = celsius_to_fahrenheit(100)
print(temp_f) # 212.0

Returning Different Data Types​

Python functions can return any data type—there are no restrictions on what you return.

Returning Numbers​

def calculate_total(price, tax_rate):
"""Returns the total price including tax."""
return price * (1 + tax_rate)

total = calculate_total(100, 0.08)
print(total) # 108.0

Returning Strings​

def greet(name):
"""Returns a personalized greeting."""
return f"Hello, {name}!"

message = greet("Alice")
print(message) # "Hello, Alice!"

Returning Booleans​

def is_even(number):
"""Returns True if number is even, False otherwise."""
return number % 2 == 0

print(is_even(4)) # True
print(is_even(7)) # False

Returning Collections (Lists, Dicts)​

def get_user_profile(user_id):
"""Returns a dictionary with user information."""
return {
"id": user_id,
"name": "John Doe",
"email": "[email protected]"
}

profile = get_user_profile(1)
print(profile["name"]) # "John Doe"
print(profile["email"]) # "[email protected]"

def get_fibonacci(n):
"""Returns first n Fibonacci numbers as a list."""
fib = [0, 1]
for i in range(2, n):
fib.append(fib[-1] + fib[-2])
return fib

numbers = get_fibonacci(8)
print(numbers) # [0, 1, 1, 2, 3, 5, 8, 13]

Returning Multiple Values Using Tuples​

Python's tuple unpacking allows returning multiple values from a single function. When you comma-separate values in a return statement, Python automatically packages them into a tuple:

def get_user_info(user_id):
"""Returns username, age, and active status."""
return "alice", 28, True

# Unpack the tuple into separate variables
username, age, is_active = get_user_info(1)
print(f"{username} is {age} years old") # "alice is 28 years old"
print(f"Active: {is_active}") # "Active: True"

Partial Unpacking​

You don't have to unpack all values:

def get_file_info(filename):
"""Returns filename, size (bytes), and creation date."""
return filename, 2048, "2026-06-02"

# Capture only what you need
name, size, _ = get_file_info("data.txt")
print(f"{name}: {size} bytes") # "data.txt: 2048 bytes"

Or Capture the Entire Tuple​

def calculate_stats(numbers):
"""Returns min, max, and average."""
return min(numbers), max(numbers), sum(numbers) / len(numbers)

stats = calculate_stats([10, 20, 30, 40])
print(stats) # (10, 40, 25.0)
print(f"Min: {stats[0]}, Max: {stats[1]}, Avg: {stats[2]}")

Understanding Implicit Return: When a Function Returns None​

Every Python function returns something. If you don't write an explicit return statement, the function implicitly returns None (a special value representing "nothing"):

def say_hello(name):
"""This function doesn't return anything."""
print(f"Hello, {name}!")

result = say_hello("Bob")
print(result) # None
print(type(result)) # <class 'NoneType'>

Checking for None​

def find_value(data, key):
"""Returns value if key exists, otherwise implicitly returns None."""
if key in data:
return data[key]
# If key not found, function reaches end and returns None

result = find_value({"name": "Alice"}, "age")
if result is None:
print("Key not found") # Prints this

Common Pattern: Return None Explicitly​

def validate_email(email):
"""Returns email if valid, None if invalid."""
if "@" in email and "." in email:
return email
else:
return None

email = validate_email("[email protected]")
if email:
print(f"Valid: {email}")
else:
print("Invalid email")

Writing Cleaner Code with Early Returns​

Early returns (multiple return statements) validate inputs and exit early, reducing nested if statements and improving readability.

The Problem: Nested If Statements​

def process_order(order):
"""Processes an order—nested if version."""
if order is not None:
if isinstance(order, dict):
if "total" in order:
if order["total"] > 0:
return f"Processing order for ${order['total']}"
else:
return "Error: Order total must be positive"
else:
return "Error: Order missing 'total' field"
else:
return "Error: Order must be a dictionary"
else:
return "Error: Order is None"

Deep nesting makes this hard to follow.

The Solution: Early Returns​

def process_order(order):
"""Processes an order—early return version."""
# Validate: order exists
if order is None:
return "Error: Order is None"

# Validate: order is a dict
if not isinstance(order, dict):
return "Error: Order must be a dictionary"

# Validate: order has 'total' field
if "total" not in order:
return "Error: Order missing 'total' field"

# Validate: total is positive
if order["total"] <= 0:
return "Error: Order total must be positive"

# Happy path: all validations passed
return f"Processing order for ${order['total']}"

# Test cases
print(process_order(None)) # "Error: Order is None"
print(process_order("invalid")) # "Error: Order must be a dictionary"
print(process_order({"item": "book"})) # "Error: Order missing 'total' field"
print(process_order({"total": -10})) # "Error: Order total must be positive"
print(process_order({"total": 49.99})) # "Processing order for $49.99"

This "flat" structure is much easier to read and modify.


Multiple Return Statements: Control Flow with Return​

You can have many return statements in a function. Execution stops at the first one reached:

def categorize_age(age):
"""Categorizes age into groups."""
if age < 13:
return "Child"
elif age < 18:
return "Teenager"
elif age < 65:
return "Adult"
else:
return "Senior"

print(categorize_age(10)) # "Child"
print(categorize_age(16)) # "Teenager"
print(categorize_age(30)) # "Adult"
print(categorize_age(70)) # "Senior"

Returning Different Types Based on Condition​

def lookup_value(data, key):
"""Returns value if key exists, False if not, for clarity."""
if key in data:
return data[key]
return False # Distinct from None

result = lookup_value({"name": "Alice"}, "name")
print(result) # "Alice"

result = lookup_value({"name": "Alice"}, "age")
print(result) # False

Frequently Asked Questions​

Can a function have multiple return statements?​

Yes. Execution stops at the first return encountered. Use multiple returns for validation (early return) or conditional logic (if/elif/else with different returns).

def get_status(code):
if code == 200:
return "OK"
if code == 404:
return "Not Found"
if code == 500:
return "Server Error"
return "Unknown"

What's the difference between return and print?​

print() displays output to the console but doesn't send a value to the caller. return sends a value back to wherever the function was called. Use return for data; use print() for debug messages or user-facing output.

def add(a, b):
result = a + b
print(result) # Shows: 30
return result # Sends: 30 back

value = add(10, 20)
print(value) # Shows: 30 (the returned value)

Do I have to use the return value?​

No. If you call a function that returns a value but don't capture it, the value is discarded:

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

get_sum(5, 10) # Return value is discarded; nothing happens
result = get_sum(5, 10) # Return value is captured

Can I return without a value?​

Yes. return without a value (or return None explicitly) exits the function and returns None:

def log_message(msg):
if not msg:
return # Exit early, return None
print(f"Log: {msg}")

log_message("") # Returns None, prints nothing
log_message("Event occurred") # Prints "Log: Event occurred", returns None

What happens if a function has no return statement at all?​

It implicitly returns None when execution reaches the end:

def no_return():
print("Hello")

result = no_return()
print(result) # None

Further Reading​