How to Return Multiple Values from a Function in Python

To return multiple values from a function in Python, list them after return, separated by commas. Python packs them into one tuple, and the caller unpacks it into separate variables:

def low_high(nums):
    return min(nums), max(nums)

low, high = low_high([3, 9, 1])

I tested every example here on Python 3.12.5 in the Command Prompt, so the outputs you see are real.

Below, I’ll cover unpacking tricks, dictionaries, NamedTuple and dataclass results, lists and generators, type hints, and how to pick the right one.

How to return multiple values from a Python function with a tuple

This function sums order prices and hands back three results, which the caller catches in one line:

def order_summary(prices):
    total = sum(prices)
    average = total / len(prices)
    return len(prices), total, average     # three values, one return

count, total, average = order_summary([24.99, 120.00, 8.50, 46.25])
print(f"Orders:  {count}")
print(f"Total:   ${total:,.2f}")
print(f"Average: ${average:,.2f}")

result = order_summary([10.00, 30.00])
print(type(result).__name__, result)

Output:

Orders:  4
Total:   $199.74
Average: $49.94
tuple (2, 40.0, 20.0)
Command Prompt output of a Python function returning an order count, total and average unpacked into three variables
The three returned values land in count, total and average, and the last line shows the raw result is a tuple.

Without unpacking, you get a single tuple object. It’s the commas that build it, not parentheses, so return a, b and return (a, b) are the same.

If you want more depth on tuples themselves, like returning a tuple of tuples, see how to return a tuple in Python.

Does Python really return more than one value?

Not quite. A function always returns exactly one object. When you write several values after return, that object is a tuple, and unpacking is what makes it feel like several results.

For how return itself behaves, including the implicit None, read my guide to the Python return statement.

Unpack returned values with *, _ and a ValueError check

When a function returns more values than you need, use *name to collect the extras into a list, or _ as a throwaway name. Get the count wrong and Python raises ValueError:

def city_temps():
    return "Denver", 41, 68, 55          # city, low, high, average (F)

city, *temps = city_temps()              # * collects the rest into a list
print(city, temps)

city, _, high, _ = city_temps()          # _ for values you don't need
print(f"{city} high: {high}F")

try:
    city, low = city_temps()             # 4 values, only 2 names
except ValueError as err:
    print("ValueError:", err)

Output:

Denver [41, 68, 55]
Denver high: 68F
ValueError: too many values to unpack (expected 2)
Command Prompt output of Python star and underscore unpacking of returned values plus a ValueError for too many values
Star unpacking puts the three temperatures in a list, the underscore version keeps only the high, and two names for four values raise ValueError.

There are more patterns, like unpacking inside a for loop, in unpacking tuples in Python.

Return a dictionary when the values need names

A tuple works for two or three values, but quote[2] tells a reader nothing. A dictionary labels every value, so the caller picks results by key:

def shipping_quote(weight_lb, zone):
    rate = 4.75 if zone == "domestic" else 18.40
    cost = round(weight_lb * rate, 2)
    return {"cost": cost, "carrier": "UPS", "days": 3 if zone == "domestic" else 8}

quote = shipping_quote(2.5, "domestic")
print(quote)
print(f"${quote['cost']:.2f} via {quote['carrier']}, {quote['days']} days")

Output:

{'cost': 11.88, 'carrier': 'UPS', 'days': 3}
$11.88 via UPS, 3 days

I reach for a dict when the result has optional fields or gets passed straight to JSON. The trade-off is that a typo in a key only fails at runtime.

Returning values is also the clean way to get data out of a function without globals; accessing variables outside a function in Python compares the options.

Return a NamedTuple or dataclass for structured results

For results you’ll use in many places, give them a real type. NamedTuple adds field names to a tuple, and a @dataclass builds a small class with a readable repr:

from typing import NamedTuple
from dataclasses import dataclass

class Stats(NamedTuple):
    low: float
    high: float
    mean: float

@dataclass
class Invoice:
    customer: str
    subtotal: float
    tax: float

def price_stats(prices):
    return Stats(min(prices), max(prices), round(sum(prices) / len(prices), 2))

def make_invoice(customer, subtotal, rate=0.0825):
    return Invoice(customer, subtotal, round(subtotal * rate, 2))

s = price_stats([19.99, 5.49, 42.00])
print(s)
low, high, mean = s                      # still unpacks like a tuple
print(f"High: ${s.high:.2f}")

inv = make_invoice("Megan Bowen", 250.00)
print(inv)
print(f"Total due: ${inv.subtotal + inv.tax:.2f}")

Output:

Stats(low=5.49, high=42.0, mean=22.49)
High: $42.00
Invoice(customer='Megan Bowen', subtotal=250.0, tax=20.62)
Total due: $270.62
Command Prompt output of Python functions returning a NamedTuple of price stats and a dataclass invoice
The NamedTuple prints its field names and still unpacks, and the dataclass invoice shows the customer, subtotal and tax.

A NamedTuple is immutable and still unpacks, so tuple callers don’t break. A dataclass is mutable and easier to extend.

The full side-by-side is in Python dataclass vs namedtuple.

Return a list or generator when the count varies

If you don’t know how many values come back, return a list. If the values are produced one by one, a generator with yield hands them out lazily:

def over_budget(expenses, limit):
    return [name for name, amount in expenses.items() if amount > limit]

def running_totals(amounts):
    total = 0.0
    for amount in amounts:
        total += amount
        yield round(total, 2)            # one value at a time

expenses = {"rent": 1850.00, "groceries": 420.75, "travel": 610.00, "phone": 65.00}
print(over_budget(expenses, 500))
print(list(running_totals([100.00, 49.95, 250.10])))

Output:

['rent', 'travel']
[100.0, 149.95, 400.05]

over_budget() returned the two categories over $500.00, and the generator gave three running totals. Generators save memory, but you can loop over them only once.

Add type hints to multiple return values

Type hints tell readers and tools like mypy what comes back. Use tuple[str, str] for a fixed-size tuple, and | None when nothing might be found:

def split_name(full_name: str) -> tuple[str, str]:
    first, _, last = full_name.partition(" ")
    return first, last

def find_user(user_id: int) -> tuple[str, int] | None:
    users = {101: ("Alex Wilber", 34), 102: ("Adele Vance", 29)}
    return users.get(user_id)

print(split_name("Adele Vance"))
print(find_user(101), find_user(999))
print(split_name.__annotations__["return"])

Output:

('Adele', 'Vance')
('Alex Wilber', 34) None
tuple[str, str]
Command Prompt output of Python functions with tuple return type hints, including a None result
split_name() returns a first and last name, find_user(999) returns None, and the annotation prints as tuple[str, str].

Python doesn’t enforce these hints at runtime. For more on typing tuples, see Python type hints for tuples.

Which way should you return multiple values?

Return typeBest forCaller reads
Tuple2-3 fixed valuesa, b = f()
DictionaryLabeled or optional fieldsr['cost']
NamedTupleFixed, immutable recordsr.high or unpack
DataclassRecords with methods or changesr.tax
List / generatorUnknown number of valuesfor x in f()

Any of these beats writing results into globals. If you’re still doing that, setting global variables in Python functions shows why it gets messy.

Next, you might like these function guides:

Frequently asked questions

How do I return two values from a function in Python?

Write return a, b, then unpack with x, y = my_function(). Python returns them as one tuple.

Can a Python function return a tuple and a list together?

Yes. return my_tuple, my_list returns a tuple whose two items are your tuple and your list.

Why do I get ValueError: too many values to unpack?

The function returned more values than you have names. Match the count, or use *rest or _ for the extras.

Should I return a tuple or a dictionary?

Use a tuple for two or three values with an obvious order. Use a dictionary, NamedTuple or dataclass when the values need names.

How do I type hint multiple return values?

Use -> tuple[int, str] for a fixed tuple, or return a NamedTuple class and use it as the return type.

Where are tuples and unpacking documented?

See Tuples and Sequences in the official Python tutorial.