Explain Codes LogoExplain Codes Logo

Alternatives for returning multiple values from a Python function

python
functions
dataframe
pandas
Anton ShumikhinbyAnton Shumikhin·Dec 9, 2024
TLDR

Pack multiple values into a tuple for seamless function returns in Python:

def calc_stats(): return (4, 3.5) # Returns a tuple with count and average count, avg = calc_stats() print(count, avg) # 4 3.5

Unpacking converts these values back into separate variables in one clear, concise operation.

For more sophisticated use-cases, consider named tuples from the collections module, or the stronger typing.NamedTuple in Python 3.6+, which bring readability and structured access:

from collections import namedtuple # Define a named tuple Stats = namedtuple('Stats', ['count', 'average']) def advanced_calc(): return Stats(count=4, average=3.5) # NamedTuple magic! stats = advanced_calc() print(stats.count, stats.average) # Where's my 3.5? Oh, there it is.

Named tuples offer immutability, aid in forming self-documenting code, and can function as a bridge between the simplicity of tuples and the formality of a full class.

More than just tuples, return VaLueS gAlorE!

Sometimes, unpacking tuples can feel as messy as spaghetti code, especially if you're dealing with multiple return values. When your tuple starts feeling like a bundle of snakes, here are some stretchy solutions.

Transport heavy data with a class

Assembling a custom class with attributes can handle substantial amounts of data with behaviour:

class Report: def __init__(self, summary, details): self.summary = summary self.details = details # details, always the details... def generate_report(): summary = "Annual Sales" details = {"Q1": 25000, "Q2": 40000} # So many numbers... return Report(summary, details) # "It's alive, ALIVE!"

Say bye to order woes with a dictionary

Dictionaries shine where return tuple sequences can get hazy and prone to errors due to their ordered nature:

def user_profile(): return {'username': 'johndoe', 'access_level': 'admin', 'id': 1029} profile = user_profile() print(profile['username']) # 'JohnDoe', not 'janeDoe'

Unleash the power of generators for large sequences

Pull the levers of generators whenever you need to wrangle large datasets or generate a series of values on-demand:

def get_prime_numbers(limit): for num in range(2, limit): if all(num % i != 0 for i in range(2, int(num ** 0.5) + 1)): yield num # "I got the power!" primes = get_prime_numbers(10) print(list(primes)) # Prime time!

Named Tuples to the rescue

Meet the typing.NamedTuple for type annotations, docstrings, and default values:

from typing import NamedTuple class Employee(NamedTuple): name: str id: int = 3 # Default ID, because we're not in middleware anymore # Example usage employee = Employee('Guido') assert employee.id == 3 # Guido, forever #3 in our 💖

It's a lunchbox with labeled compartments—no more guessing what's under the lid!

Functional programming, no strings attached

A functional approach favors immutable types and thus tuples. Named tuples extend the benefits by improving readability and condensing code structure.