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