When defining functions, you learned how to specify parameters that accept arguments when the function is called. Sometimes, however, you want a function parameter to have a "standard" or "default" value if the caller doesn't provide one. This makes the function more flexible, as callers only need to supply arguments for parameters whose values differ from the default.
Python allows you to define default values for parameters directly in the function definition.
To give a parameter a default value, you assign it a value using the assignment operator (=
) in the def
statement.
def greet(name, greeting="Hello"):
"""Prints a greeting.
Args:
name: The name of the person to greet.
greeting: The greeting message to use (defaults to "Hello").
"""
print(f"{greeting}, {name}!")
# Calling the function
greet("Alice") # Uses the default greeting
greet("Bob", "Good morning") # Overrides the default greeting
Output:
Hello, Alice!
Good morning, Bob!
In this example, the greeting
parameter has a default value of "Hello"
.
greet("Alice")
, we only provide the name
argument. Python sees that the greeting
argument is missing and automatically uses its default value, "Hello"
.greet("Bob", "Good morning")
, we provide both arguments. The value "Good morning"
overrides the default value for greeting
.A significant rule when defining functions with default arguments is that all parameters with default values must come after all parameters without default values.
Why? When you call a function using positional arguments (arguments passed in order without specifying the parameter name), Python matches the values to parameters from left to right. If a non-default parameter came after a default one, Python wouldn't know whether a provided argument was meant for the non-default parameter or intended to override the default value of the earlier parameter.
Correct:
def create_user(username, is_active=True, permissions="read"):
print(f"Creating user: {username}")
print(f"Active: {is_active}")
print(f"Permissions: {permissions}")
Incorrect: This will cause a SyntaxError
.
# SyntaxError: non-default argument follows default argument
# def create_user_incorrect(is_active=True, username, permissions="read"):
# pass
You can call functions with default arguments in several ways:
Provide only required arguments: Python uses the defaults for the others.
create_user("charlie")
# Output:
# Creating user: charlie
# Active: True
# Permissions: read
Provide arguments positionally: Values override defaults in order.
create_user("david", False) # Overrides is_active, uses default permissions
# Output:
# Creating user: david
# Active: False
# Permissions: read
create_user("eve", False, "admin") # Overrides both defaults
# Output:
# Creating user: eve
# Active: False
# Permissions: admin
Provide arguments using keywords: This allows you to override specific defaults regardless of their position (as long as required arguments are met). This is often clearer.
create_user("frank", permissions="write") # Overrides only permissions
# Output:
# Creating user: frank
# Active: True
# Permissions: write
create_user(username="grace", is_active=False) # Uses keyword arguments
# Output:
# Creating user: grace
# Active: False
# Permissions: read
Default arguments are frequently used for:
verbose=False
flag to enable extra output.timeout=30
seconds for a network operation.tolerance=0.001
for numerical comparisons.There's a subtle but important detail to be aware of: default argument values are evaluated only once, when the function is defined, not each time the function is called. This is fine for immutable types like numbers, strings, or tuples. However, if you use a mutable type like a list or dictionary as a default value, that same object will be used across all calls that rely on the default. This can lead to unexpected results.
Consider this example:
def add_item(item, my_list=[]):
"""Adds an item to a list. (Demonstrates mutable default issue)"""
my_list.append(item)
print(f"List is now: {my_list}")
print("First call:")
add_item("apple")
print("\nSecond call:")
add_item("banana") # Unexpectedly adds to the list from the first call!
print("\nThird call, providing a list:")
add_item("cherry", ["start"]) # Works as expected when a list is provided
Output:
First call:
List is now: ['apple']
Second call:
List is now: ['apple', 'banana']
Third call, providing a list:
List is now: ['start', 'cherry']
Notice how the second call, add_item("banana")
, didn't start with an empty list. It used the same list that was modified in the first call. This is rarely the intended behavior.
The Standard Solution:
The conventional way to handle mutable defaults is to use None
as the default value and then create a new mutable object inside the function if the parameter is None
.
def add_item_safe(item, my_list=None):
"""Adds an item to a list safely."""
if my_list is None:
my_list = [] # Create a new list if none was provided
my_list.append(item)
print(f"List is now: {my_list}")
print("First call (safe):")
add_item_safe("apple")
print("\nSecond call (safe):")
add_item_safe("banana") # Starts with a new empty list
print("\nThird call, providing a list (safe):")
add_item_safe("cherry", ["start"])
Output:
First call (safe):
List is now: ['apple']
Second call (safe):
List is now: ['banana']
Third call, providing a list (safe):
List is now: ['start', 'cherry']
Using None
as a placeholder default and creating the mutable object within the function ensures that each call relying on the default gets a fresh instance, avoiding unintended side effects between calls.
Default argument values are a powerful feature for making your functions more convenient and adaptable, but remember the distinction between mutable and immutable defaults to avoid common pitfalls.
© 2025 ApX Machine Learning