How to use 'return' in Python
Learn how to use the return statement in Python. Explore different methods, tips, real-world applications, and how to debug common errors.

The return statement is a fundamental part of Python functions. It allows you to send a value back from a function, which makes your code more modular and reusable.
In this article, we'll cover techniques to use return effectively. You'll find practical tips, see real-world applications, and get advice to debug common issues so you can master this key statement.
Using return to output values from functions
def add_numbers(a, b):
result = a + b
return result
sum_result = add_numbers(5, 3)
print(sum_result)--OUTPUT--8
In the example, the add_numbers function computes a sum and sends it back using the return result statement. This is the key step that makes the function's output available to the rest of your script. Without return, the calculated value would be lost once the function finishes running.
The line sum_result = add_numbers(5, 3) demonstrates how to capture this output. The function executes, returns the value 8, and this value is then stored in the sum_result variable. This allows you to reuse the results of your functions throughout your code.
Returning different data types
The return statement isn't limited to single values; you can also use it to send back multiple values, entire collections, or even other functions.
Returning multiple values with return
def get_person_details():
name = "Alice"
age = 30
city = "New York"
return name, age, city
name, age, city = get_person_details()
print(f"{name} is {age} years old and lives in {city}")--OUTPUT--Alice is 30 years old and lives in New York
The get_person_details function shows how you can send back multiple items. Just list them after the return keyword, separated by commas. Python automatically bundles these values into a tuple behind the scenes.
- When you call the function, you can unpack this tuple directly into separate variables.
- The line
name, age, city = get_person_details()assigns each value from the returned tuple to the corresponding variable in order.
Returning collections like dictionaries
def create_user_profile():
profile = {
"username": "jsmith",
"email": "john@example.com",
"active": True
}
return profile
user = create_user_profile()
print(user["username"], user["email"])--OUTPUT--jsmith john@example.com
Your functions aren't limited to simple data types; they can return complex structures like dictionaries. In the example, create_user_profile assembles a user profile and returns the entire dictionary. It’s a clean way to bundle related data together.
- The returned dictionary is captured in the
uservariable. - You can then access its contents using familiar dictionary keys, such as
user["email"].
Returning functions with return
def create_multiplier(factor):
def multiply(number):
return number * factor
return multiply
double = create_multiplier(2)
triple = create_multiplier(3)
print(double(5), triple(5))--OUTPUT--10 15
Python's flexibility allows functions to return other functions, creating powerful, reusable code. The create_multiplier function acts as a factory—it defines and returns a nested function called multiply.
- This returned
multiplyfunction "remembers" the originalfactorit was created with, a concept known as a closure. - When you call
create_multiplier(2), you get a new function that will always multiply its input by 2. You can assign this function to a variable likedoubleand call it whenever you need it.
Advanced return techniques
With the fundamentals of the return statement covered, you can now enhance your functions with conditional logic, generators, and type hints to handle more complex scenarios.
Using conditional return statements
def check_number(num):
if num > 0:
return "Positive"
elif num < 0:
return "Negative"
return "Zero"
print(check_number(10))
print(check_number(-5))
print(check_number(0))--OUTPUT--Positive
Negative
Zero
The check_number function shows how you can control a function's output based on specific conditions. It uses if and elif statements to evaluate the input num and decide which string to send back.
- When a condition like
num > 0is met, itsreturnstatement runs, and the function immediately stops executing. - If neither the
ifnor theelifcondition is true, the code proceeds to the final line, returning"Zero"as a default. This pattern ensures your function always returns a predictable value.
Using return with generators
def countdown(n):
while n > 0:
yield n
n -= 1
return "Liftoff!"
generator = countdown(3)
for value in generator:
print(value)--OUTPUT--3
2
1
In a generator function like countdown, the yield keyword pauses execution and sends back a value. The for loop then resumes the function to get the next value. This continues until the generator is exhausted.
- Notice how the loop prints
3,2, and1, which are the values passed toyield. - The
return "Liftoff!"statement signals the end of the generator's execution. However, its value isn't captured by theforloop, which is why you don't see "Liftoff!" in the output.
Adding type hints to return statements
def divide(a: float, b: float) -> float:
if b == 0:
return float('inf') # Return infinity for division by zero
return a / b
print(divide(10, 2))
print(divide(5, 0))--OUTPUT--5.0
inf
Type hints make your code clearer and easier to debug. In the divide function, the -> float annotation signals that it’s designed to return a floating-point number. This acts as a helpful guide for anyone using your function, including automated code analysis tools.
- The function handles division by zero by returning
float('inf'), which is a special floating-point value representing infinity. - This ensures that even in edge cases, the function's output type remains consistent with the
-> floathint.
Move faster with Replit
Replit is an AI-powered development platform that comes with all Python dependencies pre-installed, so you can skip setup and start coding instantly. This lets you move from learning individual techniques, like how to use the return statement, to building complete applications with Agent 4.
Instead of piecing together functions, you can describe the app you want to build. Agent handles writing the code, connecting to APIs, and managing deployment based on your description. For example, you could have it build:
- A dynamic pricing utility that uses function factories to generate custom discount calculators for different sales campaigns.
- A data parsing tool that unpacks user records from a list and returns the details as a structured dictionary for an admin dashboard.
- A content validator that checks user input against a set of rules and returns a status like 'Approved', 'Pending', or 'Rejected'.
Simply describe your app, and Replit will write the code, test it, and fix issues automatically, all within your browser.
Common errors and challenges
Using the return statement effectively means avoiding a few common traps, like forgetting it entirely or failing to capture the value it sends back.
Forgetting to return values from functions
It's a classic mistake: your function calculates a value perfectly, but you forget to add a return statement. When this happens, the function returns None by default, which can cause unexpected bugs. The following code shows this exact problem in action.
def calculate_discount(price, percent):
discount = price * (percent / 100)
final_price = price - discount
# Missing return statement
item_price = 100
discount_percent = 20
sale_price = calculate_discount(item_price, discount_percent)
print(f"Sale price: ${sale_price}") # Prints: Sale price: $None
Because the calculate_discount function doesn't explicitly return the final_price, the sale_price variable is assigned None. This simple oversight is what causes the incorrect output. The corrected code below addresses this.
def calculate_discount(price, percent):
discount = price * (percent / 100)
final_price = price - discount
return final_price # Added return statement
item_price = 100
discount_percent = 20
sale_price = calculate_discount(item_price, discount_percent)
print(f"Sale price: ${sale_price}") # Prints: Sale price: $80.0
The fix is simple—adding return final_price ensures the calculate_discount function sends back the computed value. This allows the sale_price variable to correctly capture the result instead of defaulting to None.
- This kind of bug is common in functions that perform calculations but don't have an explicit return path. Always double-check that your functions send back the data you expect them to produce.
Not capturing the return value
A function can return a value, but that output is lost if you don't assign it to a variable. This oversight can lead to bugs where your code proceeds on a false assumption. The following example shows this with the validate_username function.
def validate_username(username):
if len(username) < 3:
return False
return True
# Function is called but return value is ignored
validate_username("ab")
# Later we assume validation passed
print("Username is valid, proceeding...")
The script calls validate_username but ignores its False return value, creating a logic error by assuming the username is valid. The corrected code below shows how to properly handle the function's output.
def validate_username(username):
if len(username) < 3:
return False
return True
# Capture and check the return value
is_valid = validate_username("ab")
if is_valid:
print("Username is valid, proceeding...")
else:
print("Username is invalid, please try again.")
The fix is to capture the function's output in a variable, like is_valid. This lets you use the returned value to control your program's logic, ensuring your code doesn't proceed with invalid data.
- By checking
is_validin anifstatement, the code can now react correctly, preventing it from proceeding with an invalid username. - This is especially important for functions that perform checks or validations, as ignoring their output can lead to silent, hard-to-find bugs.
Unexpected behavior with return in loops
Using a return statement inside a loop can be tricky. If you're not careful, it can make your function exit before it has checked all the items, leading to incorrect results. This often happens with a misplaced else clause.
The following code shows how the find_first_even function returns None because the loop terminates on the very first item, which is odd.
def find_first_even(numbers):
for num in numbers:
if num % 2 == 0:
return num
else:
return None # This causes early return!
result = find_first_even([1, 3, 5, 6, 8])
print(f"First even number: {result}") # Incorrectly prints None
The else statement incorrectly triggers a return on the first odd number, ending the function before it can find an even one. The following code demonstrates the correct way to structure the loop.
def find_first_even(numbers):
for num in numbers:
if num % 2 == 0:
return num
return None # Only return None after checking all numbers
result = find_first_even([1, 3, 5, 6, 8])
print(f"First even number: {result}") # Correctly prints 6
The fix is to move the return None statement outside the loop. This allows the find_first_even function to check every number in the list before deciding a value can't be found. The function now only returns None after the loop finishes, ensuring it completes its search instead of exiting prematurely.
- This is a key pattern to use whenever you're searching for an item and need to handle cases where it might not exist.
Real-world applications
Now that you can sidestep common pitfalls, you can use the return statement to build robust features for data validation and memoization.
Using return for data validation
You can use the return statement to create robust validation functions that provide clear feedback by returning both a status and a descriptive message.
def validate_password(password):
if len(password) < 8:
return False, "Password must be at least 8 characters"
if not any(char.isdigit() for char in password):
return False, "Password must contain at least one number"
return True, "Password is valid"
is_valid, message = validate_password("pass123")
print(f"Valid: {is_valid}, Message: {message}")
is_valid, message = validate_password("securepassword123")
print(f"Valid: {is_valid}, Message: {message}")
The validate_password function uses a "fail-fast" approach, running checks sequentially. It's an efficient way to handle validation because the function exits as soon as a rule is broken.
- If a password fails the first check, the function immediately returns a
Falsestatus and a corresponding error message. - Only if a password passes all checks does the function reach its final
returnstatement to confirm validity.
You can then unpack the returned tuple, giving you separate variables for the validation status and the message.
Implementing a memoization decorator with return
You can use the return statement to create a memoization decorator, which caches a function's results and returns them instantly on subsequent calls.
def memoize(func):
cache = {}
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrapper
@memoize
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
# First call (calculates and caches)
print(f"Result: {fibonacci(30)}")
# Second call (returns from cache)
print(f"Cached result: {fibonacci(30)}")
The @memoize decorator enhances the fibonacci function by adding a caching layer. It defines a wrapper function that intercepts calls and uses a dictionary to store results.
- When you call
fibonacci(30), thewrapperfirst checks its internalcache. - If the result for that input isn't there, it runs the original function, saves the output to the cache, and then returns it.
- On any future call with the same input, it skips the expensive calculation and returns the stored value directly.
Get started with Replit
Put your knowledge of the return statement into practice. Tell Replit Agent to build "a currency converter that returns exchange rates" or "a data validator that returns a status and error message."
Replit Agent writes the code, tests for errors, and deploys the app for you. It’s that simple. Start building with Replit.
Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.
Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.



