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)

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)

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

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]

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 type | Best for | Caller reads |
|---|---|---|
| Tuple | 2-3 fixed values | a, b = f() |
| Dictionary | Labeled or optional fields | r['cost'] |
| NamedTuple | Fixed, immutable records | r.high or unpack |
| Dataclass | Records with methods or changes | r.tax |
| List / generator | Unknown number of values | for 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:
- Use Python functions with optional arguments
- Use the Python main function with arguments
- Difference between *args and **kwargs
- Python dataclass guide
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.

Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn