Default and Flexible Arguments in Python Functions

Data Science Tool Box Mar 18, 2026

Default and Flexible Arguments in Python Functions

Using default and flexible arguments effectively is a key skill for writing clean, reusable, and user-friendly functions in data science. In 2026, modern Python practices emphasize clear defaults, keyword-only arguments, and controlled flexibility.

TL;DR — Best Practices

  • Use sensible default values for optional parameters
  • Place default arguments after non-default ones
  • Use keyword-only arguments (after *) for important configuration options
  • Avoid mutable default arguments (lists, dicts, etc.)

1. Good Default Arguments Example

from typing import List, Optional
import pandas as pd

def preprocess_data(
    df: pd.DataFrame,
    target_column: str,
    drop_na: bool = True,                    # Good default
    scale_features: bool = False,            # Sensible default
    test_size: float = 0.2,                  # Common default in ML
    random_state: Optional[int] = 42         # Reproducibility default
) -> dict:
    """
    Preprocess data for machine learning.
    """
    if drop_na:
        df = df.dropna(subset=[target_column])
    
    # Further preprocessing logic...
    
    return {
        "processed_df": df,
        "test_size": test_size,
        "random_state": random_state
    }

2. Using Keyword-Only Arguments (Recommended for Configuration)

def train_model(
    X_train,
    y_train,
    *,
    model_type: str = "random_forest",      # Keyword-only
    n_estimators: int = 100,                # Keyword-only
    max_depth: Optional[int] = None,        # Keyword-only
    random_state: int = 42                  # Keyword-only
):
    """
    Train a model with clear configuration parameters.
    Using * forces users to use keyword arguments for configuration.
    """
    if model_type == "random_forest":
        from sklearn.ensemble import RandomForestClassifier
        model = RandomForestClassifier(
            n_estimators=n_estimators,
            max_depth=max_depth,
            random_state=random_state
        )
    # ... other model types
    
    model.fit(X_train, y_train)
    return model

3. Avoiding Mutable Default Arguments (Important Gotcha)

# ❌ Bad practice - mutable default argument
def bad_function(data=[]):        # Never do this!
    data.append("item")
    return data

# ✅ Correct way
def good_function(data=None):
    if data is None:
        data = []
    data.append("item")
    return data

4. Best Practices in 2026

  • Provide sensible, commonly used default values
  • Place all default parameters after non-default parameters
  • Use keyword-only arguments (after *) for configuration options to improve clarity
  • Never use mutable objects (lists, dicts, sets) as default values
  • Use Optional[...] from typing when a parameter can be None
  • Document default behavior clearly in the docstring

Defaults and Flexible Arguments

Default arguments let callers skip common options, while *args collects extra positional values into a tuple and **kwargs collects extra keyword values into a dictionary. Together they let a function stay simple for typical calls and flexible for special ones.

A common pattern is passing **kwargs through to a library function, such as forwarding plotting options to pandas. Document which keys are expected so callers know what is allowed.

Worked Example: Forwarding Options

A helper accepts any number of columns and forwards extra options to round.

import pandas as pd

def mean_of(df, *columns, decimals=1, **kwargs):
    cols = list(columns) or df.select_dtypes("number").columns.tolist()
    return df[cols].mean(**kwargs).round(decimals)

df = pd.DataFrame({"a": [1, 2, 3], "b": [4.0, None, 6.0], "c": ["x", "y", "z"]})
print(mean_of(df, "a"))
print(mean_of(df, "a", "b", decimals=2))
print(mean_of(df, skipna=False))

With no columns given, the helper uses every numeric column. skipna=False is passed straight through to mean, so column b returns NaN because of its missing value.

Practical Tips

A useful safety measure is to validate kwargs against an allowed set, for example by raising a TypeError for unknown keys. That gives callers the same protection against typos that normal named parameters provide, while keeping the flexibility of forwarding options to a library.

Keep the number of defaults modest as well. A function with a dozen optional parameters is hard to learn, and splitting it into two focused functions is often clearer.

Common Mistakes

  • Using mutable defaults such as lists.
  • Swallowing misspelled keywords silently in **kwargs.
  • Putting *args after keyword-only parameters by mistake.
  • Not documenting which kwargs are forwarded.

Quick Summary

  • Defaults simplify common calls.
  • *args gathers positional extras.
  • **kwargs gathers keyword extras.
  • Forward kwargs carefully and document them.

Conclusion

Default and flexible arguments, when used correctly, make your data science functions much more convenient and professional. In 2026, the standard is to combine sensible defaults, keyword-only parameters for configuration, and careful avoidance of mutable defaults. These practices result in functions that are easier to use, less error-prone, and more maintainable.

Next steps:

  • Review your current data science functions and improve their parameter design using default values and keyword-only arguments 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