🕯️ Magic Note
When you write return a, b, c, Python actually returns a single tuple containing (a, b, c). The parentheses are optional. The comma is what creates the tuple. This is called tuple packing. The caller’s x, y, z = func() unpack the tuple into separate variables.
Python
# Return multiple values as a tuple
def get_min_max(numbers):
return min(numbers), max(numbers)
# Unpack the returned tuple into variables
minimum, maximum = get_min_max([1, 2, 3, 4, 5])
print(minimum, maximum) # 1 5
# You can also capture the tuple itself
result = get_min_max([1, 2, 3, 4, 5])
print(result) # (1, 5)
print(type(result)) # <class ‘tuple’>
Python
# 1. Statistical functions (min, max, average, etc.)
def analyze_scores(scores):
return min(scores), max(scores), sum(scores) / len(scores)
lowest, highest, avg = analyze_scores([85, 92, 78, 90, 88])
print(f”Low: {lowest}, High: {highest}, Avg: {avg:.1f}”)
# 2. Status and result (like Go language style)
def divide(a, b):
if b == 0:
return False, 0 # success flag, result
return True, a / b
success, result = divide(10, 2)
if success:
print(f”Result: {result}”)
else:
print(“Division failed”)
# 3. Finding and returning a value with its index
def find_first_occurrence(items, target):
for i, item in enumerate(items):
if item == target:
return i, item
return -1, None
index, value = find_first_occurrence([10, 20, 30, 20, 40], 20)
print(f”Found {value} at index {index}”) # Found 20 at index 1
# 4. Swapping values (already uses tuple packing/unpacking)
x, y = 5, 10
x, y = y, x # Pythonic swap
print(x, y) # 10 5
🕯️ Magic Note
The pattern return success, result is common in languages like Go but is not idiomatic Python. Python programmers usually raise exceptions for errors. However, it can be useful when failures are expected and common (like searching) and you want to avoid exception overhead.
Python
def get_stats(numbers):
return min(numbers), max(numbers), sum(numbers) / len(numbers)
# Ignore min, keep max and avg
_, maximum, average = get_stats([1, 2, 3, 4, 5])
print(maximum, average) # 5 3.0
# Ignore both min and max, keep only avg
*_, average = get_stats([1, 2, 3, 4, 5])
# Or
_, _, avg = get_stats([1, 2, 3, 4, 5])
# The underscore is a convention for “unused”
Python
def get_stats(numbers):
sorted_nums = sorted(numbers)
return sorted_nums[0], sorted_nums[1], sorted_nums[-2], sorted_nums[-1], sum(numbers) / len(numbers)
# Capture first, second, last, second-last, and average
first, second, second_last, last, avg = get_stats([1, 2, 3, 4, 5, 6, 7, 8, 9])
# Or use star to capture everything between
first, second, *middle, second_last, last = get_stats([1, 2, 3, 4, 5, 6, 7, 8, 9])
print(first, second, middle, second_last, last)
Python
from collections import namedtuple
# Define a named tuple type
Statistics = namedtuple(“Statistics”, [“minimum”, “maximum”, “average”, “count”])
def calculate_stats(numbers):
return Statistics(
minimum=min(numbers),
maximum=max(numbers),
average=sum(numbers) / len(numbers),
count=len(numbers)
)
stats = calculate_stats([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
# Access by name (clear and self-documenting)
print(f”Minimum: {stats.minimum}”)
print(f”Maximum: {stats.maximum}”)
print(f”Average: {stats.average}”)
print(f”Count: {stats.count}”)
# Also works with unpacking
min_val, max_val, avg_val, cnt = stats
🕯️ Magic Note
Named tuples are regular tuples as well. They support indexing and unpacking. But they also give you named attributes. This is the best of both worlds: the efficiency of a tuple with the clarity of a class.
Python
from dataclasses import dataclass
from typing import List
@dataclass
class AnalysisResult:
minimum: float
maximum: float
average: float
median: float
outliers: List[float]
def is_normal_range(self):
return self.average – self.median < 0.5 * (self.maximum – self.minimum)
def analyze_data(data):
sorted_data = sorted(data)
n = len(sorted_data)
median = sorted_data[n // 2] if n % 2 else (sorted_data[n//2 – 1] + sorted_data[n//2]) / 2
return AnalysisResult(
minimum=min(data),
maximum=max(data),
average=sum(data) / n,
median=median,
outliers=[x for x in data if x > max(data) or x < min(data)]
)
result = analyze_data([1, 2, 3, 100, 4, 5, 6])
print(f”Median: {result.median}”)
print(f”Normal range? {result.is_normal_range()}”)
Python
# Returning a dictionary (self-documenting)
def get_user_info(user_id):
return {
“name”: “Ali Rezaei”,
“age”: 25,
“city”: “Tehran”,
“is_active”: True
}
info = get_user_info(123)
print(info[“name”]) # Ali Rezaei
# When to use dict vs tuple:
# – Tuple: Small number of values (2-5), order is meaningful
# – Named tuple: Small number of values, but names add clarity
# – Dict: Many values, or values may be added in the future
# – Dataclass: Complex return with methods or validation
Python
from typing import Tuple, Union, Optional
# Simple tuple return type hint
def divide(a: float, b: float) -> Tuple[bool, float]:
if b == 0:
return False, 0.0
return True, a / b
# Tuple with mixed types
def find_user(uid: int) -> Tuple[Optional[str], Optional[int]]:
# Returns (name, age) or (None, None) if not found
if uid == 1:
return “Ali”, 25
return None, None
# Named tuple type hint (using the type itself)
def analyze(numbers: List[float]) -> Statistics:
return Statistics(min=min(numbers), max=max(numbers),
avg=sum(numbers)/len(numbers), count=len(numbers))
- Forgetting to unpack and getting a tuple instead of separate values
- Trying to unpack into mismatched numbers of variables (ValueError)
- Returning too many values (more than 5-6 becomes hard to remember)
- Modifying a returned mutable value that the function still uses
- Using regular tuple when named tuple would be clearer
Python
# Common mistake example
def bad_return():
return 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 # Too many values
# Mistake: Forgetting to unpack correctly
result = bad_return() # result is a tuple, not 10 separate variables
a, b, c, d, e, f, g, h, i, j = bad_return() # Works but verbose
# Better: Return a named tuple for many values
ManyValues = namedtuple(“ManyValues”, “a b c d e f g h i j”)
def good_return():
return ManyValues(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
vals = good_return()
print(vals.a, vals.b, vals.c) # Clear and readable
- How do you return three values from a function?
- What happens if you unpack a 3-value tuple into 2 variables?
- How do you ignore a returned value?
- What is the advantage of a named tuple over a regular tuple?
- Write a function that returns (min, max, sum) of a list of numbers.
- When would you return a dictionary instead of a tuple?
⚡ Whisper
Returning multiple values is like giving someone a small bundle. You tie several things together with a invisible thread: the comma. The caller receives the bundle and opens it, each item falling into its own waiting hand. This is efficient, elegant, and uniquely Pythonic. No special syntax. No wrapper classes. Just commas and assignment. But be careful. The bundle can grow heavy. If you return more than five or six values, the order becomes hard to remember. That is when you need a named bundle: a named tuple with labels on each item. Or a dictionary. Or a dataclass. The comma is for small bundles. Names are for larger ones. Choose the right wrapping for your gift. Your callers will appreciate the thought.