Python  

How to Return More Than One Value from a Function in Python

Table of Contents

  1. Introduction

  2. Why Return Multiple Values?

  3. Real-World Scenario: Real-Time Health Monitoring in Wearable Devices

  4. Methods to Return Multiple Values

  5. Complete Implementation with Test Cases

  6. Best Practices for Clean and Maintainable Code

  7. Conclusion

1. Introduction

In many programming languages, functions are limited to returning a single value. But Python empowers developers with elegant, built-in ways to return multiple values—making code more expressive, efficient, and aligned with real-world logic.

This article explores practical techniques to return multiple results from a function, grounded in a compelling use case from wearable health tech, and delivers production-ready, thoroughly tested code.

2. Why Return Multiple Values?

Real-world operations rarely produce just one output. Consider:

  • A sensor reading that returns heart rate, oxygen level, and confidence score

  • A financial calculation that yields profit, tax, and net amount

  • A machine learning model that outputs predictions, probabilities, and processing time

Returning multiple values avoids:

  • Global variables

  • Mutable side effects

  • Artificial wrapper objects for simple cases

Python makes this natural—no boilerplate required.

3. Real-World Scenario: Real-Time Health Monitoring in Wearable Devices

Modern smartwatches continuously monitor users’ vital signs. Every 10 seconds, a function processes raw sensor data and must return:

  • Heart rate (BPM)

  • SpOâ‚‚ level (%)

  • Signal quality (0–100%)

This trio is used by:

  • The UI to display live stats

  • An alert system to trigger medical warnings

  • A cloud sync service to log trends

PlantUML Diagram

Returning all three in one call ensures atomicity, consistency, and performance, critical in life-critical systems.

4. Methods to Return Multiple Values

1. Return a Tuple

def read_vitals():
    return 72, 98, 95  # heart_rate, spo2, quality

2. Return a Dictionary

def read_vitals():
    return {
        "heart_rate": 72,
        "spo2": 98,
        "quality": 95
    }

3. Return a Named Tuple (Best of Both Worlds)

from collections import namedtuple
Vitals = namedtuple('Vitals', ['heart_rate', 'spo2', 'quality'])

def read_vitals():
    return Vitals(72, 98, 95)

4. Return a Dataclass (For Complex or Evolving Data)

from dataclasses import dataclass

@dataclass
class Vitals:
    heart_rate: int
    spo2: int
    quality: int

def read_vitals():
    return Vitals(72, 98, 95)

Rule of thumb: Use tuples for simple, fixed-size returns; named tuples or dataclasses for clarity and scalability.

5. Complete Implementation with Test Cases

PlantUML Diagram
from collections import namedtuple
import unittest

# Define a clean return type
Vitals = namedtuple('Vitals', ['heart_rate', 'spo2', 'quality'])

def process_sensor_data(raw_signal: list) -> Vitals:
    """
    Simulates processing raw PPG sensor data from a wearable.
    Returns heart rate (BPM), SpO2 (%), and signal quality (0-100).
    """
    if not raw_signal:
        return Vitals(0, 0, 0)
    
    # Simplified logic: in reality, this involves FFT, filtering, etc.
    avg = sum(raw_signal) / len(raw_signal)
    heart_rate = min(180, max(40, int(avg * 0.8 + 60)))
    spo2 = min(100, max(80, int(avg * 0.5 + 85)))
    quality = min(100, max(0, int(avg * 2)))
    
    return Vitals(heart_rate, spo2, quality)

class TestMultiValueReturn(unittest.TestCase):
    def test_normal_reading(self):
        raw = [100, 105, 98, 102, 101]
        vitals = process_sensor_data(raw)
        self.assertIsInstance(vitals, Vitals)
        self.assertGreater(vitals.heart_rate, 40)
        self.assertLessEqual(vitals.spo2, 100)  # Fixed: allow 100
        self.assertGreaterEqual(vitals.quality, 0)

    def test_empty_input(self):
        vitals = process_sensor_data([])
        self.assertEqual(vitals, Vitals(0, 0, 0))

    def test_unpacking(self):
        hr, spo2, qual = process_sensor_data([90])
        expected_hr = min(180, max(40, int(90*0.8 + 60)))  # 132
        expected_spo2 = min(100, max(80, int(90*0.5 + 85)))  # 100 (capped)
        expected_qual = min(100, max(0, int(90*2)))  # 100 (capped)
        
        self.assertEqual(hr, expected_hr)
        self.assertEqual(spo2, expected_spo2)
        self.assertEqual(qual, expected_qual)

if __name__ == "__main__":
    # Simulate real-time reading
    raw_data = [95, 97, 96, 94, 98]
    hr, spo2, quality = process_sensor_data(raw_data)
    
    print(f"  Heart Rate: {hr} BPM")
    print(f" SpOâ‚‚: {spo2}%")
    print(f" Signal Quality: {quality}/100")
    
    if quality < 70:
        print(" Warning: Poor signal quality!")
    if spo2 < 90:
        print(" Critical: Low oxygen saturation!")
    
    # Run tests
    unittest.main(argv=[''], exit=False, verbosity=2)
1

6. Best Practices for Clean and Maintainable Code

  • Prefer named tuples or dataclasses over plain tuples when readability matters.

  • Never return inconsistent types (e.g., sometimes tuple, sometimes dict).

  • Document return types using type hints (-> Vitals).

  • Use unpacking for clean caller code: a, b, c = func()

  • Avoid returning too many values (>4)—consider a class or split into multiple functions.

  • In performance-critical paths, plain tuples are fastest; for APIs, use structured types.

7. Conclusion

Returning multiple values isn’t just a Python convenience—it’s a design enabler for systems that mirror real-world complexity. In health tech, getting heart rate, oxygen, and quality in one atomic call ensures reliability and responsiveness. By choosing the right return strategy—tuple, named tuple, or dataclass—you balance simplicity, clarity, and scalability. Remember: good functions don’t just compute—they communicate. And in Python, they can say more than one thing at once.