
Python
Default parameters provide automatic values if no argument is given. They enable clean APIs and backward compatibility while reducing boilerplate code. Understanding edge cases and best practices around mutable defaults is crucial for writing robust Python code.
Default parameters provide automatic values if no argument is given:
def greet(name="User"):
return f"Hello, {name}!"
print(greet()) # Uses default: "Hello, User!"
print(greet("Alice")) # Uses provided: "Hello, Alice!"Configuration functions are where defaults shine. They let you provide sensible, commonly-used defaults while allowing full customization when needed. This pattern is used extensively in APIs and libraries.
def create_api_config(model="GPT-4", temperature=0.7, max_tokens=1000):
return {
"model": model,
"temperature": temperature,
"max_tokens": max_tokens,
}
config1 = create_api_config() # All defaults
config2 = create_api_config("Claude", 0.3) # Override some
config3 = create_api_config(max_tokens=2000) # Named overrideNon-default parameters must come before parameters with defaults. This is a language requirement that prevents ambiguity in positional arguments.
# ✅ Correct
def func(required, optional="default"):
pass
# ❌ Error - defaults must come last
def func(optional="default", required):
passThis is a critical gotcha. Defaults are evaluated once when the function is defined, not each time the function is called. This causes unexpected behavior with mutable defaults like lists or dictionaries.
# Tricky example
def add_to_list(item, my_list=[]): # ⚠️ Dangerous!
my_list.append(item)
return my_list
print(add_to_list(1)) # [1]
print(add_to_list(2)) # [1, 2] - list persists!
print(add_to_list(3)) # [1, 2, 3] - same list!
# Better approach
def add_to_list(item, my_list=None):
if my_list is None:
my_list = []
my_list.append(item)
return my_listNever use mutable objects as defaults. Each function call shares the same mutable object, leading to unexpected state persistence. This is one of Python's most infamous gotchas.
# ❌ All calls share the same dictionary
def log_event(name, log={}):
log[name] = True
return log
print(log_event("login")) # {'login': True}
print(log_event("logout")) # {'login': True, 'logout': True}
# ✅ Create new dictionary for each call
def log_event(name, log=None):
if log is None:
log = {}
log[name] = True
return logdef create_user(name, role="user", permissions=None):
if permissions is None:
# Set default permissions based on role
permissions = {
"admin": ["read", "write", "delete"],
"user": ["read"],
"guest": []
}[role]
return {
"name": name,
"role": role,
"permissions": permissions
}
print(create_user("Alice")) # Default guest
print(create_user("Bob", "admin")) # Admin role
print(create_user("Charlie", "user", ["read", "write"])) # Customdef connect_to_database(
host="localhost",
port=None,
database="default",
timeout=None
):
# Port defaults based on whether it's local
if port is None:
port = 5432 if host == "localhost" else 3306
# Timeout defaults based on host
if timeout is None:
timeout = 5 if host == "localhost" else 30
return {
"host": host,
"port": port,
"database": database,
"timeout": timeout
}Modern Python uses type hints to clarify expected types:
def create_config(
model: str = "GPT-4",
temperature: float = 0.7,
max_tokens: int = 1000,
settings: dict = None
) -> dict:
if settings is None:
settings = {}
return {
"model": model,
"temperature": temperature,
"max_tokens": max_tokens,
**settings
}1. Use None for mutable defaults — Always use `None` as the default for lists, dicts, sets
2. Document defaults — Use docstrings to explain default behavior
3. Keep defaults sensible — Choose values that work for 80% of use cases
4. Avoid side effects — Don't have defaults that modify external state
def process_data(
data: list = None,
output_file: str = "output.txt",
verbose: bool = False
) -> None:
"""
Process data and save results.
Args:
data: Input data to process (default: empty list)
output_file: Path to save results (default: 'output.txt')
verbose: Print processing details (default: False)
"""
if data is None:
data = []
# Process data...
pass| Concept | Remember |
|---|---|
| Defaults evaluated once | At function definition time |
| Mutable defaults dangerous | Use None and initialize in body |
| Order matters | Required before default parameters |
| Type hints helpful | Clarify expected types and defaults |
| Document | Explain default behavior |
| Sensible defaults | Choose 80% use case values |
Now let's learn about *args — accepting variable numbers of arguments!
Next: Variable Arguments (*args) →
Ready to practice? Try advanced challenges
Resources
Ojasa Mirai
Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.
Learn Deep • Build Real • Verify Skills • Launch Forward