Python Default Arguments: Simplify Your Functions

Learn how to use default arguments in Python to create flexible and readable functions. Simplify your code and reduce redundancy with this essential feature.

4.2 Default Arguments in Python

Python allows you to define functions with parameters that have default values. These default values are automatically used when an argument for that parameter is not provided during a function call. This feature simplifies function usage, enhances code readability, and reduces redundancy.

What are Default Arguments?

A default argument is a parameter that automatically assigns a predefined value if no value is explicitly passed for it when calling the function. This makes parameters optional and leads to more flexible function definitions.

Why Use Default Arguments?

  • Optional Parameters: Makes certain function parameters optional, allowing for more flexible function calls.
  • Reduces Redundancy: Eliminates the need for function overloading for common scenarios.
  • Simplifies Code: Makes code cleaner, more concise, and easier to understand.
  • Enhances Readability: Clearly indicates expected behavior when arguments are omitted.

Syntax of Default Arguments

The syntax for defining a function with default arguments is as follows:

def function_name(param1, param2=default_value):
    # Function body
    pass

Important Rule: Parameters with default values must appear after all parameters without default values in the function definition.

Examples of Default Arguments

Example 1: Basic Usage

This example demonstrates a show_profile function where the country parameter has a default value of "India".

def show_profile(name, country="India"):
    """Displays a user's profile information."""
    print("Name:", name)
    print("Country:", country)

# Calling with both arguments:
print("--- Calling with both arguments ---")
show_profile("Rahul", "USA")

print("\n--- Calling with only one argument ---")
# Calling with only one argument (uses the default country):
show_profile("Karan")

Output:

--- Calling with both arguments ---
Name: Rahul
Country: USA

--- Calling with only one argument ---
Name: Karan
Country: India

Explanation:

  • The first call show_profile("Rahul", "USA") provides a value for country, overriding the default "India".
  • The second call show_profile("Karan") does not provide a value for country, so the default value "India" is used.

Example 2: Default Argument in a Mathematical Function

Here, calculate_percentage uses a default total of 100.

def calculate_percentage(obtained, total=100):
    """Calculates the percentage of obtained marks."""
    if total == 0:
        return 0.0  # Avoid division by zero
    result = (obtained / total) * 100
    return result

# Using the default total:
print("--- Using default total ---")
print("Percentage:", calculate_percentage(75))

# Overriding the default total:
print("\n--- Overriding default total ---")
print("Percentage:", calculate_percentage(160, 200))

Output:

--- Using default total ---
Percentage: 75.0

--- Overriding default total ---
Percentage: 80.0

Explanation:

  • calculate_percentage(75) uses the default total=100.
  • calculate_percentage(160, 200) provides 200 for total, overriding the default.

Important Caveat: Mutable Default Arguments

A critical point to remember is how Python evaluates default argument values. They are evaluated only once when the function is defined, not each time the function is called. If the default value is a mutable object (like a list or dictionary), modifications made to this default object within the function will persist across subsequent calls. This can lead to unexpected and hard-to-debug behavior.

Problematic Example with Mutable Default Argument

Consider a function designed to append a number to a list. Using an empty list as a default leads to a shared, persistent list.

def add_number_problematic(num, num_list=[]):
    """Appends a number to a list (problematic with default mutable)."""
    num_list.append(num)
    print(num_list)

print("--- Problematic Mutable Default ---")
add_number_problematic(5)
add_number_problematic(10)
add_number_problematic(15)

Output:

--- Problematic Mutable Default ---
[5]
[5, 10]
[5, 10, 15]

Explanation of the Problem: In this case, all calls add_number_problematic(5), add_number_problematic(10), and add_number_problematic(15) are modifying the same default list instance that was created when the function was defined.

Best Practice: Use None for Mutable Defaults

The standard and recommended practice to avoid the mutable default argument problem is to use None as the default value and then initialize a new mutable object inside the function if None is passed.

def add_number_safe(num, num_list=None):
    """Appends a number to a list safely."""
    if num_list is None:
        num_list = []  # Initialize a new list if None was passed
    num_list.append(num)
    print(num_list)

print("\n--- Safe Mutable Default with None ---")
add_number_safe(5)
add_number_safe(10)
add_number_safe(15)

# Demonstrating passing a pre-existing list
my_list = [100]
print("\n--- Safe with pre-existing list ---")
add_number_safe(200, my_list)
add_number_safe(300, my_list)

Output:

--- Safe Mutable Default with None ---
[5]
[10]
[15]

--- Safe with pre-existing list ---
[100, 200]
[100, 200, 300]

Explanation of the Solution: By using num_list=None and then checking if num_list is None: num_list = [], we ensure that a new list is created for each function call where no list is explicitly provided. This prevents side effects and maintains the expected behavior of isolated function calls. When you do want to pass a specific list, you can do so, and the function will correctly append to that provided list.

Key Takeaways

  • Reduced Redundancy: Default arguments make code cleaner by reducing the need for repeated arguments or function overloading.
  • Order Matters: Parameters with default values must always follow parameters without default values in the function signature.
  • Avoid Mutable Defaults: Never use mutable objects (lists, dictionaries, sets) directly as default arguments.
  • Use None Pattern: For mutable defaults, use None as the default and initialize the mutable object within the function body.

Frequently Asked Questions (FAQs)

  • Q1: Can we define multiple default arguments in a function? Yes, multiple parameters can have default values, but they must all come after any non-default parameters.

    def configure_settings(host, port=8080, timeout=30, debug=False):
        pass
  • Q2: What happens if we override all default arguments? The default values are completely ignored, and the values provided during the function call are used for all parameters.

  • Q3: Are default arguments required in all function calls? No, they are optional. You can call a function with default arguments by providing values for them or by omitting them to use the defaults.

  • Q4: Is it mandatory to provide default values from the end of the parameter list? Yes, absolutely. This is a strict rule in Python. All parameters with default values must be trailing in the parameter list, following any parameters that do not have defaults.

Interview Questions

  • What are default arguments in Python, and how do they work?
  • Why are default arguments useful in Python functions?
  • Can you provide a simple example of a Python function with a default argument?
  • What is the outcome when you override a default argument during a function call?
  • Explain why using mutable default arguments (like lists or dictionaries) is considered a bad practice.
  • How can you safely and effectively use mutable objects as default values for function parameters in Python?
  • Can a Python function have multiple parameters with default values? If so, how are they defined?
  • In what order must default and non-default arguments appear in a function's parameter list?
  • How does Python evaluate default argument values, and what are the implications?
  • Describe the potential issues and expected output when using the same mutable object (e.g., a list) as a default parameter in multiple function calls.