Table of contents
Implement code functionality

How to use 'return' in Python

May 30, 2025
 ・ by  
Claude and the Anthropic Team
Table of contents
H2 Link Template
Try Claude

The return statement in Python enables functions to send data back to the code that called them. Understanding how to effectively use return helps you write cleaner, more modular code that processes and shares information efficiently.

This guide covers essential techniques, practical tips, and real-world applications for mastering return statements. The code examples were created with Claude, an AI assistant built by Anthropic, to demonstrate proper implementation.

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)
8

The add_numbers function demonstrates a fundamental use of return statements. When the function completes its calculation, return result sends the sum back to the calling code, making the value available for further use or storage in variables.

This pattern enables clean separation of concerns in your programs. The function handles the computation internally while making its output accessible externally. Without a return statement, the calculated sum would remain trapped inside the function—inaccessible to the rest of your program.

Returning different data types

Python's return statement can output more than just single values—it handles multiple values, complex data structures, and even entire functions with equal elegance.

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}")
Alice is 30 years old and lives in New York

The get_person_details() function showcases Python's ability to return multiple values in a single statement. When you use return name, age, city, Python automatically packs these values into a tuple.

You can unpack the returned values directly into separate variables using parallel assignment: name, age, city = get_person_details(). This clean syntax eliminates the need for accessing tuple indices manually.

  • The number of variables on the left must match the number of returned values
  • Variable names can differ from those inside the function
  • The order of assignment matches the order of returned values

This pattern proves especially useful when a function needs to provide multiple related pieces of data while maintaining clean, readable code.

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"])
jsmith john@example.com

The create_user_profile() function demonstrates how dictionaries make excellent return values for organizing related data. Instead of returning multiple separate values, it packages user information into a single, structured dictionary object.

  • Each key-value pair in the returned dictionary represents a distinct user attribute
  • The calling code can easily access specific data points using dictionary keys
  • This approach scales well when you need to add or modify user properties

When the function returns the profile dictionary, you maintain direct access to all user data through the user variable. This pattern proves particularly valuable when working with complex data structures or API responses that require organized data handling.

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))
10 15

The create_multiplier() function demonstrates a powerful Python feature called function factories. It creates and returns customized functions on demand instead of just returning data.

  • The outer function create_multiplier accepts a factor parameter that determines the multiplication behavior
  • The inner function multiply uses this factor to perform the actual calculation
  • When you call create_multiplier(2), it returns a new function that always multiplies by 2

This pattern enables you to generate specialized functions with preset behaviors. The returned functions maintain access to their creation context through closure. Each new function operates independently with its own multiplication factor.

Advanced return techniques

Building on these foundational techniques, Python's return statement unlocks even more sophisticated patterns through conditional logic, generator functions, and type annotations.

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))
Positive
Negative
Zero

The check_number() function demonstrates how multiple return statements create efficient branching logic. Each condition evaluates the input and immediately returns the appropriate string result, eliminating the need for storing temporary variables.

  • The function checks conditions in order: positive numbers first, then negative numbers
  • The final return "Zero" acts as a default case when no other conditions match
  • Once a return statement executes, the function stops immediately. This prevents unnecessary condition checking

This pattern creates cleaner, more maintainable code compared to storing results in variables. The immediate returns make the function's logic flow easy to follow and modify.

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)
3
2
1

The countdown() function demonstrates how generators can work alongside return statements. While yield produces values one at a time during iteration, the return statement marks the generator's completion point.

  • Each yield statement temporarily pauses the function's execution. It resumes from that point when the generator requests the next value
  • The return value "Liftoff!" isn't directly accessible through normal iteration. It signals the generator's final state
  • The for loop processes each yielded value (3, 2, 1) but doesn't capture the returned string

This pattern enables memory-efficient processing of sequences. The generator creates values on demand instead of storing the entire sequence in memory at once.

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))
5.0
inf

Type hints enhance code clarity by explicitly declaring the expected input and output types. The -> arrow syntax after the function parameters specifies that divide() will return a float value. This helps other developers understand the function's behavior at a glance.

  • The : float annotations tell Python that both a and b parameters should be floating-point numbers
  • Modern code editors use these hints to catch type-related errors before running the code
  • Type hints serve as built-in documentation without affecting runtime performance

The function handles division by zero gracefully by returning float('inf') instead of raising an error. This design choice maintains the promised return type while providing a mathematically appropriate result.

Get unstuck faster with Claude

Claude is an AI assistant from Anthropic that excels at helping developers write, debug, and understand code. It combines deep technical knowledge with natural conversation to provide clear, accurate guidance on programming challenges.

When you encounter tricky Python concepts like generator functions or type hints, Claude can break them down step by step. It helps you understand error messages, suggests code improvements, and explains complex programming patterns in simple terms.

Start accelerating your Python development today. Sign up for free at Claude.ai to get personalized coding assistance and level up your programming skills with an AI mentor that adapts to your learning style.

Some real-world applications

Building on the advanced techniques we've explored, the return statement enables practical solutions for everyday programming challenges like data validation and performance optimization.

Using return for data validation

The return statement enables robust data validation by sending back both a success status and detailed feedback messages, as demonstrated in the validate_password function that checks password strength requirements.

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 Python's ability to return multiple values while implementing password validation logic. It checks two key requirements: the password must be at least 8 characters long and contain at least one number.

The function returns a tuple containing a boolean status and a descriptive message. When validation fails, it immediately returns False with the specific reason. If all checks pass, it returns True with a success message.

  • The any() function efficiently checks for numeric characters
  • Tuple unpacking (is_valid, message = ...) cleanly separates the return values
  • Early returns create clear control flow by exiting at the first failure

Implementing a memoization decorator with return

The memoize decorator leverages Python's return statement to cache function results, dramatically speeding up recursive operations by storing previously calculated values in memory.

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 optimizes function performance by storing previously calculated results in a dictionary cache. When you call a memoized function, it first checks if the arguments exist as a key in the cache. If found, it returns the cached result instead of recalculating.

This technique particularly benefits recursive functions like fibonacci. Without memoization, calculating fibonacci(30) would redundantly compute the same values many times. The decorator intercepts these repeated calculations by returning cached results.

  • The wrapper function captures function arguments as dictionary keys
  • The cache dictionary persists between function calls
  • Each unique input only triggers one actual calculation

Common errors and challenges

Even experienced Python developers encounter common pitfalls with return statements that can lead to subtle bugs and unexpected program behavior.

Forgetting to return values from functions

A missing return statement creates one of the most common yet subtle bugs in Python functions. When you forget to explicitly return a value, Python automatically returns None. This default behavior can silently propagate through your code and cause unexpected results.

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 sends it back to the calling code. Since Python implicitly returns None, the sale_price variable receives no actual value. Let's examine the corrected version below.

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

Adding the return final_price statement ensures the function sends back the calculated discount price to the calling code. Without an explicit return, Python automatically returns None. This can cause errors when you try to perform operations on the returned value.

  • Always check if your functions return the expected values
  • Use print statements or a debugger to verify return values during development
  • Pay special attention to functions that perform calculations or data transformations

This issue commonly appears in larger functions with multiple code paths. Make sure each path includes appropriate return statements to handle all possible scenarios.

Not capturing the return value

Another critical mistake occurs when developers call functions but ignore their returned values. The return statement sends back crucial information that your code needs to make decisions. Failing to capture and use these values can create dangerous assumptions in your program flow.

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 code ignores the validate_username() function's False return value, which indicates invalid input. The program continues executing as if validation succeeded. Check the corrected implementation below that properly handles the validation result.

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 corrected code stores the validate_username() function's return value in the is_valid variable. This enables proper validation flow control through a conditional statement that checks the boolean result. When the username fails validation, the code executes the appropriate error handling path instead of proceeding incorrectly.

  • Always capture return values when you need to make decisions based on function results
  • Watch for implicit boolean checks in conditional statements
  • Pay special attention when working with validation functions that return boolean values

This pattern proves especially important in user input validation, API response handling, and data processing workflows where function results determine program flow.

Unexpected behavior with return in loops

Misplaced return statements inside loops can prematurely exit functions before processing all elements. A common mistake occurs when developers place a return in the else clause of a conditional within a loop, preventing the function from checking subsequent values.

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 return None statement inside the else clause executes immediately after checking the first number. This prevents the function from examining the remaining values in the list. The corrected implementation appears below.

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

Moving the return None statement outside the loop fixes the premature exit issue. The function now properly checks all numbers in the list before concluding no even numbers exist. This pattern ensures complete iteration through the data.

  • Watch for return statements inside loop bodies that might trigger too early
  • Consider whether your function needs to process all elements or stop at the first match
  • Place default return values after loops complete their iterations

This error commonly appears in search functions, data validation, and list processing operations. Always test your functions with various inputs to verify they examine all necessary elements.

Learning or leveling up? Use Claude

Claude combines advanced language understanding with deep programming expertise to serve as your personal coding companion. The AI assistant helps you master Python concepts through interactive explanations and real-time code analysis, adapting its guidance to match your skill level.

  • Debug return values: Ask "Why does my function return None instead of the calculated result?" and Claude will help identify missing return statements or logic errors in your code.
  • Return type guidance: Ask "What's the best return type for a function that processes user data?" and Claude will explain how to choose between dictionaries, tuples, or custom objects.
  • Refactoring help: Ask "How can I improve this function that returns multiple values?" and Claude will suggest cleaner implementations using modern Python features.
  • Error resolution: Ask "Why am I getting 'NoneType' errors?" and Claude will explain how implicit returns affect your code and recommend fixes.
  • Best practices: Ask "What are common mistakes with return statements?" and Claude will share practical tips to write more reliable functions.

Ready to accelerate your Python development? Visit Claude.ai to start coding with an AI assistant that understands your needs and helps you write better code.

For seamless integration into your development workflow, Claude Code brings AI-powered assistance directly to your terminal—enabling faster debugging, code reviews, and implementation guidance without leaving your preferred environment.

FAQs

Additional Resources

How to do absolute value in Python

2025-05-30
14 min
 read
Read more

How to do square root in Python

2025-05-22
14 min
 read
Read more

Improve code maintainability using Claude

2025-05-30
6 min
 read
Read more

Leading companies build with Claude

ReplitCognitionGithub CopilotCursorSourcegraph
Try Claude
Get API Access
Copy
Expand