Return Values from Functions in Python

Data Science Tool Box Mar 18, 2026

Return Values from Functions in Python

How you return values from functions significantly impacts code clarity, reusability, and maintainability. In 2026, modern data science code follows clear conventions for returning data from functions — especially when working with Pandas DataFrames, models, metrics, and pipelines.

TL;DR — Modern Return Value Best Practices

  • Return clean, consistent data types
  • Use type hints for return values
  • Prefer returning tuples or dataclasses for multiple values
  • Avoid returning None unless explicitly indicating failure

1. Basic Return Patterns

from typing import Tuple, Optional
import pandas as pd

def process_sales_data(df: pd.DataFrame) -> pd.DataFrame:
    """Clean and enrich sales data."""
    cleaned = df.dropna(subset=["amount", "customer_id"]).copy()
    cleaned = cleaned.assign(
        year=cleaned["order_date"].dt.year,
        profit=cleaned["amount"] * 0.25
    )
    return cleaned


def calculate_metrics(df: pd.DataFrame) -> Tuple[float, float, int]:
    """Return multiple metrics as a tuple."""
    total_sales = df["amount"].sum()
    avg_sale = df["amount"].mean()
    transaction_count = len(df)
    return total_sales, avg_sale, transaction_count

2. Recommended Patterns for Data Science

from dataclasses import dataclass
from typing import Dict, Any

@dataclass
class ModelResults:
    model: Any
    accuracy: float
    precision: float
    recall: float
    feature_importance: Dict[str, float]

def train_and_evaluate(
    X_train, y_train, X_test, y_test
) -> ModelResults:
    """Train model and return structured results."""
    # ... model training logic ...
    
    return ModelResults(
        model=model,
        accuracy=0.87,
        precision=0.85,
        recall=0.89,
        feature_importance=feature_importance_dict
    )


def load_and_clean_data(file_path: str) -> Optional[pd.DataFrame]:
    """Return cleaned DataFrame or None if loading fails."""
    try:
        df = pd.read_csv(file_path, parse_dates=["order_date"])
        # cleaning logic...
        return df
    except Exception as e:
        print(f"Error loading data: {e}")
        return None

3. Best Practices for Return Values in Data Science 2026

  • Use type hints for all return values (especially with complex returns)
  • Return tuples when returning multiple related values
  • Use dataclasses or namedtuples for structured return values
  • Be consistent — if a function sometimes returns None, make it clear in the docstring and type hint
  • Prefer returning rich objects (DataFrames, dataclasses) over raw primitives when it improves clarity
  • Document what is returned, especially when returning multiple values

Designing Return Values

A return statement sends a result back to the caller and ends the function. A function without return gives back None, which is a common source of bugs when the caller expects data.

Return one consistent type on every path. If a function sometimes returns a DataFrame and sometimes a string message, every caller must check which one it got. Raise an exception for errors instead, and return a dictionary or named tuple when several values belong together.

Worked Example: Consistent Returns

A function returns a named tuple of statistics and raises an error for empty input.

from typing import NamedTuple

class Stats(NamedTuple):
    count: int
    mean: float
    maximum: float

def describe(values: list[float]) -> Stats:
    if not values:
        raise ValueError("values must not be empty")
    return Stats(len(values), sum(values) / len(values), max(values))

result = describe([4.0, 8.0, 6.0])
print(result)
print(result.mean)

def no_return(x):
    x * 2

print(no_return(5))

The named tuple lets callers write result.mean instead of remembering positions. The last function forgets its return statement, so it hands back None, which is exactly the mistake to watch for.

Practical Tips

For data functions, returning a new DataFrame is usually better than modifying the input in place and returning None. It allows method chaining, makes tests simple, and avoids surprises when the same input is reused elsewhere. If a function can genuinely find nothing, such as a search with no match, return an empty DataFrame or None consistently and document that behaviour in the docstring.

Common Mistakes

  • Forgetting return and getting None.
  • Returning different types on different paths.
  • Returning error strings instead of raising exceptions.
  • Returning long unnamed tuples that are easy to unpack wrongly.

Quick Summary

  • Every function returns something, None by default.
  • Keep the return type consistent.
  • Raise exceptions for errors.
  • Use named tuples or dicts for several values.

Conclusion

Return values are a critical part of function design in data science. In 2026, the best practice is to be explicit with type hints, use structured return types (tuples or dataclasses) when returning multiple values, and keep return behavior consistent and predictable. Well-designed return values make your functions easier to use, test, and integrate into larger data science pipelines.

Next steps:

  • Review your current functions and improve their return values by adding type hints and using structured return types where appropriate

Related Tutorials

Share:
Last updated: March 2026
Built-in Functions in Python 2026 str() in Python 2026: String Conversion + Modern Formatting & Best Practices memoryview with JAX in Python 2026: Zero-Copy NumPy → JAX Array Interop + Efficient ML Examples memoryview with TensorFlow in Python 2026: Zero-Copy NumPy → Tensor Interop + GPU Pinning & ML Examples memoryview with TensorFlow in Python 2026: Zero-Copy NumPy → Tensor Interop + ML Examples memoryview with NumPy & PyTorch in Python 2026: Zero-Copy Views, Efficient Slicing & ML Interop Examples memoryview with NumPy in Python 2026: Zero-Copy Views, Efficient Slicing & Real ML Examples memoryview() in Python 2026: Zero-Copy Magic for Large Binary Data + Real Examples import() in Python 2026: How It Works, Security Risks & Modern Alternatives (importlib) zip() in Python 2026: How to Use It, strict=True Behavior & Real-World Examples vars() in Python 2026: Accessing Object Namespace + Modern Introspection Patterns type() in Python 2026: Dynamic Type Inspection & Object Creation + Modern Patterns tuple() in Python 2026: Immutable Sequences + Modern Patterns & Best Practices super() in Python 2026: Method Resolution & Modern Inheritance Patterns sum() in Python 2026: Summing Iterables + Modern Numeric Patterns & Best Practices staticmethod() in Python 2026: Static Methods, Utility Functions & Modern Best Practices sorted() in Python 2026: Sorting Iterables + Modern Patterns & Best Practices slice() in Python 2026: Creating Slice Objects + Modern Patterns & Best Practices setattr() in Python 2026: Dynamic Attribute Setting + Modern Patterns & Safety set() in Python 2026: Mutable Sets Creation + Modern Patterns & Best Practices