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.

How to use 'return' in Python
Published on: 
Fri
Feb 13, 2026
Updated on: 
Mon
Apr 13, 2026
The Replit Team

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 user variable.
  • 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 multiply function "remembers" the original factor it 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 like double and 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 > 0 is met, its return statement runs, and the function immediately stops executing.
  • If neither the if nor the elif condition 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, and 1, which are the values passed to yield.
  • The return "Liftoff!" statement signals the end of the generator's execution. However, its value isn't captured by the for loop, 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 -> float hint.

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_valid in an if statement, 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 False status and a corresponding error message.
  • Only if a password passes all checks does the function reach its final return statement 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), the wrapper first checks its internal cache.
  • 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.

Build your first app today

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.

Build your first app today

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.