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

The return statement is a core concept in Python functions. It lets a function send a value back to its caller, a key practice to write clean and reusable code.
You'll explore techniques for multiple and conditional returns, see real-world applications, and get practical advice. You will also find tips to debug common errors related to the return 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 add_numbers function, the return result statement is what makes the function's output useful. It passes the calculated sum out of the function's scope and back to the caller.
This allows the value to be captured in the sum_result variable. By returning a value, you're not just performing an action; you're producing a result that can be stored, manipulated, or passed to other functions. Without return, the calculation would happen, but the result would be lost, and sum_result would hold the value None.
Returning different data types
The return statement isn't limited to sending back single numbers; it can also output multiple values, complex data structures like dictionaries, or even entire 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 demonstrates how to send back multiple values. When you use a comma-separated list in the return statement, Python automatically packs these values into a tuple.
- The statement
return name, age, citycreates and returns a tuple holding the person's details. - You can then assign the returned tuple's items to individual variables in a single line, a technique called unpacking. This is what
name, age, city = get_person_details()does, making your code both concise and clear.
Returning collections like dictionaries
def create_user_profile():
profile = {
"username": "jsmith",
"email": "[email protected]",
"active": True
}
return profile
user = create_user_profile()
print(user["username"], user["email"])--OUTPUT--jsmith [email protected]
Functions aren't limited to returning simple values; they can also output complex data structures. In the create_user_profile function, returning a dictionary is a clean way to bundle related information together.
- This approach keeps your code organized. When you call the function, the entire dictionary is returned and can be stored in a single variable, like
user. - You can then access specific data points using their keys, such as
user["username"], making the returned data easy to work with.
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
In Python, functions can return other functions, which is a powerful way to create specialized tools. The create_multiplier function acts as a factory. It defines an inner function, multiply, that remembers the factor variable from its enclosing scope—a concept called a closure.
- Calling
create_multiplier(2)returns the entiremultiplyfunction, which is then assigned todouble. - This new
doublefunction is a specialized version that “knows” to multiply any number you give it by 2.
Advanced return techniques
Now that you've seen what functions can return, you can make them more dynamic and robust by controlling precisely when and how a return statement executes.
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
In the check_number function, return statements are placed inside conditional blocks. This allows the function to exit early and send back a value as soon as a condition is satisfied.
- The function first checks if
numis positive. If it is, the function returns"Positive"and stops right there. - If not, it checks if
numis negative. If true, it returns"Negative".
If neither of the first two conditions is met, the code proceeds to the final line, returning "Zero" by default. This pattern makes your code efficient by avoiding unnecessary checks.
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
The countdown function is a generator, which you can tell by its use of the yield keyword. Instead of running all at once, it produces values on demand. Each time the loop asks for a value, the function runs until it hits yield n, pauses, and sends n back.
- The
forloop prints each number the generator yields: 3, 2, and 1. - After the loop, the generator executes
return "Liftoff!". In a generator, this signals the end of iteration by raising aStopIterationexception. Theforloop handles this to stop but doesn't capture the return value. This is why "Liftoff!" isn't printed.
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 more readable and predictable. In the divide function, the -> float syntax explicitly declares that the function is expected to return a floating-point number. This helps you and your tools understand the function's contract before it even runs.
- This annotation doesn't change the runtime behavior; it's a guide for developers and static analysis tools to catch potential errors early.
- The function fulfills this promise by returning either the division result or
float('inf'), both of which are floats, ensuring type consistency.
Move faster with Replit
Replit is an AI-powered development platform that transforms natural language into working applications. Describe what you want to build, and Replit Agent creates it—complete with databases, APIs, and deployment.
For the return techniques explored in this article, Replit Agent can turn them into production tools:
- Build a user profile generator that uses a function to
returnmultiple values like a name, age, and location in a single call. - Create a configuration manager that returns application settings as a dictionary, making them easy to access and modify.
- Deploy a data processing utility that uses a factory function to
returnspecialized functions, like a custom multiplier or text formatter.
Describe your app idea, and Replit Agent writes the code, tests it, and fixes issues automatically. Try Replit Agent and see how quickly you can bring your concepts to life.
Common errors and challenges
While the return statement is straightforward, a few common mistakes can lead to bugs that are surprisingly tricky to spot.
- Forgetting to
returna value: A function that doesn't explicitly use areturnstatement automatically returnsNone. If you write a function to perform a calculation but forget to return the result, that value is lost. Any attempt to use the function's output will involve aNonevalue, often leading to unexpected errors. - Not capturing the
returnvalue: Simply calling a function isn't enough; you must assign its returned value to a variable to use it later. If a function returns a result but you don't capture it, that value is immediately discarded, making it unavailable for subsequent operations in your code. - Unexpected behavior with
returnin loops: Usingreturninside a loop immediately terminates the entire function—not just the current iteration. A common pitfall is placing areturnstatement where you intend to process multiple items, only to find the function exits after the very first one, preventing the loop from completing.
Forgetting to return values from functions
It's one of the most frequent slip-ups: performing a calculation inside a function but forgetting to send the result back. Because the function implicitly returns None without an explicit return statement, your program might behave unexpectedly. The following code demonstrates this.
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
The calculate_discount function computes the final_price but never passes it back. Since there's no explicit return, sale_price is assigned None by default, producing the incorrect output. The following code shows the fix.
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 its result back to the caller. This allows you to capture the computed value in the sale_price variable, preventing it from being assigned None.
- This kind of error is common when you're focused on a function's internal logic. Always double-check that any function designed to produce a value has an explicit
returnstatement to make its output available.
Not capturing the return value
A function's return value is useless if you don't capture it. When you call a function but fail to assign its result to a variable, the output is immediately discarded. This can lead to silent failures where your code proceeds on false assumptions.
The following code shows how the validate_username function's result is ignored, causing the program to behave incorrectly.
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 validate_username function returns False, but because the result isn't stored in a variable, the program incorrectly assumes the username is valid and proceeds. The corrected code shows how to prevent this logical error.
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.")
By capturing the function's output in the is_valid variable, the corrected code can use the returned True or False value in an if statement. This allows the program to react correctly based on the validation result.
- This error is easy to make when you call a function for its side effects but forget it also returns a crucial value. Always store a function's return value if your program's next steps depend on it.
Unexpected behavior with return in loops
Using a return statement inside a loop requires care. It doesn’t just exit the current iteration; it stops the entire function. A common mistake is putting return in an else clause, causing the function to exit before checking all items.
The following code demonstrates this issue, where the function incorrectly returns None because the first number it checks isn't even.
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: return None statement causes the function to exit on the first odd number it finds. This prevents the loop from ever reaching the even numbers in the list. The corrected code shows the proper approach.
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 corrected code moves the return None statement outside the loop. This allows the find_first_even function to check every number before concluding its search. The function returns an even number as soon as it finds one, but it only returns None after the entire loop has completed without a match.
- This logic is key when you're searching a collection and need to handle cases where the item you're looking for isn't there.
Real-world applications
With a solid grasp of how return works and what pitfalls to avoid, you can build powerful, real-world tools for validation and optimization.
Using return for data validation
Returning both a status and a descriptive message allows your validation functions to give clear, immediate feedback on whether data meets specific rules.
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 demonstrates an efficient validation pattern. It checks rules sequentially and uses an early return to exit immediately if a password fails a check, which avoids unnecessary processing.
- Instead of just returning
TrueorFalse, it returns a tuple containing a boolean and a descriptive string. - This allows the calling code to unpack the result into
is_validandmessagevariables. You get both the validation status and a clear reason for the outcome in a single, clean operation.
Implementing a memoization decorator with return
A memoization decorator leverages the return statement to cache the output of computationally expensive functions, allowing you to retrieve results instantly on future calls instead of re-running the entire process.
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 creating a performance-boosting wrapper around it. This wrapper uses a dictionary called cache to remember the results of previous calls.
- When you first call
fibonacci(30), the wrapper sees the input isn't in thecache. It runs the original function, stores the result, and then returns it. - On the second call, the wrapper finds the result already in the
cacheand returns it instantly, skipping the expensive recursive calculation. This is possible because the wrapper maintains access to thecachethrough a closure.
Get started with Replit
Turn your knowledge of return into a real tool. Tell Replit Agent to “build a currency converter that returns the amount and rate” or “create a user validation tool that returns a status and message.”
Replit Agent will write the code, test for errors, and deploy your app automatically. Start building with Replit and bring your ideas to life.
Create and deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.
Create & deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.



%2520in%2520Python.png)