Default parameter values are what make Python functions flexible without making them complicated to call. You have already seen them in action throughout this learning path: the print function accepts a separator parameter with a default of a single space, the sorted function accepts a reverse parameter that defaults to False, and the open function accepts a mode parameter that defaults to read-only text mode. In each case, the common use case requires no extra arguments, but the function can handle specialized needs when callers provide them. This article covers the mechanics of default values, the rules that govern their placement in parameter lists, the infamous mutable default trap, and the design patterns that make defaults a joy to use rather than a source of bugs.
The article on Python function parameters and arguments introduced the basic syntax: you write parameter=value in the function definition, and if the caller omits that argument, the default fills in. The article on positional and keyword arguments in Python showed how defaults interact with calling conventions. Now we focus exclusively on defaults: how they work, what can go wrong, and how to use them to design interfaces that are both powerful and safe.
The syntax and semantics of default values
A default value is written directly in the function signature, using an equals sign after the parameter name. The value on the right side can be any Python expression, a literal like 0 or "unknown", a computation like len([]), or even a function call. Python evaluates that expression once, at the moment the def statement executes, and stores the result as the default for that parameter:
def greet(name, greeting="Hello"):
print(f"{greeting}, {name}!")
greet("Ada") # Uses default: "Hello, Ada!"
greet("Ada", greeting="Hi") # Overrides default: "Hi, Ada!"When a caller provides an argument for a parameter with a default, the provided value replaces the default entirely. The default is not consulted, combined with, or otherwise involved in the call. When a caller omits the argument, the precomputed default value is used. This either-or behavior is straightforward for immutable defaults like strings and numbers, but it becomes important to understand deeply when we discuss mutable defaults in a later section.
Default values are stored as attributes on the function object itself, accessible through the defaults attribute for regular parameters and kwdefaults for keyword-only parameters. You rarely need to inspect these directly, but knowing that defaults are function attributes explains several behaviors: why defaults survive across calls, why they are the same object every time, and why modifying a mutable default affects future calls.
The ordering rule: required before optional
Python enforces a strict rule in function definitions: all parameters without defaults must appear before all parameters with defaults. Writing def process(a=10, b) is a syntax error. The rule exists because without it, callers could not skip the optional parameter without also skipping the required one, which would make the default useless in practice. Consider what would happen if the rule were not enforced: to call process and provide a value for b, you would need to also provide a value for a, because you cannot skip a positional argument to reach the next one. The default on a would never be used, which contradicts the purpose of having a default.
This rule extends naturally to functions with many parameters. Required parameters go on the left, then parameters with defaults, then keyword-only parameters which may or may not have defaults. The article on positional and keyword arguments covers how the slash and asterisk markers interact with this ordering, and the complete picture is that your parameter list communicates the calling convention through its structure alone, without any additional documentation.
The mutable default trap explained
The single most notorious pitfall in Python function definitions involves using a mutable object as a default value. Because default values are evaluated once at definition time, not each time the function is called, a mutable default like an empty list is a single object shared across all calls that use the default. If any call modifies that object, the modification is visible to every subsequent call:
def add_item(item, items=[]):
items.append(item)
return items
print(add_item("a")) # ['a']
print(add_item("b")) # ['a', 'b'] -- the same list persists!The second call to add_item returns a list containing both "a" and "b" because the default list was created once during the function definition, and both calls modified the same list object. The first call appended "a", and the second call appended "b" to the same list. This behavior surprises anyone who expects a fresh empty list on each call, and because the bug only manifests after multiple calls, it can go unnoticed in simple tests that call the function only once.
The standard fix uses None as the default and creates the mutable object inside the function body:
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return itemsNow each call that omits the items argument gets a new empty list, and the function behaves consistently regardless of how many times it has been called. This pattern is so universal that Python linters flag mutable default arguments as errors, and experienced Python developers check for it reflexively during code review. The None sentinel pattern works for lists, dictionaries, sets, and any other mutable type.
This trap also applies to default values created by calling functions that return mutable objects. A default like timezone=datetime.timezone.utc is safe because timezone objects are immutable. A default like created=datetime.datetime.now() is problematic because it freezes the creation time to when the function was defined rather than when it was called. The same None pattern handles this: use None as the default and call the time-sensitive function inside the body.
Designing functions with defaults
Well-designed default values make the common case easy and the advanced case possible. The common case should require no arguments beyond the obvious ones, and the function should behave sensibly. The advanced case should require only the arguments that need to change, not a full specification of every parameter. This is the principle behind Python's own standard library: sorted(my_list) sorts in ascending order by default, and sorted(my_list, reverse=True) changes one behavior without re-specifying the key function, the sorting algorithm, or any other detail.
When choosing defaults, think about what a caller who does not read the documentation would expect. A parameter named encoding should default to "utf-8", not "ascii", because UTF-8 is the modern standard and ASCII would silently corrupt non-English text. A parameter named timeout should default to a reasonable value for the operation, not to None meaning "wait forever," because an infinite wait is almost never the safe choice. Defaults encode assumptions, and assumptions that violate expectations become bugs that are hard to diagnose because the code that caused them, the default, is invisible at the call site.
Combine defaults with keyword-only parameters to create function interfaces that are both flexible and self-documenting. Placing an asterisk in the parameter list forces callers to use keyword arguments for all parameters after it, which means every non-default value they pass is labeled with the parameter name. A function that configures a database connection with a dozen optional parameters benefits enormously from this: the caller writes only the parameters they need to change, and the label on each one makes the intent clear without reference to the function definition.
Rune AI
Key Insights
- Default values let callers omit arguments; parameters with defaults must appear after all required parameters.
- Defaults are evaluated once when the function is defined, not each time it is called.
- Never use mutable objects like lists or dicts as default values; use None and create the object inside the function instead.
- Combine defaults with keyword-only parameters (using *) to create flexible, self-documenting function interfaces.
- Default values become part of the function's public API; changing a default can break code that relied on the old behavior.
Frequently Asked Questions
Why can't I put a required parameter after a parameter with a default value?
Why does Python evaluate default values only once at definition time?
Can I use a function call as a default parameter value?
Conclusion
Default parameter values are one of the most useful features in Python's function definition syntax. They let you design functions that work with minimal input while allowing callers to override any parameter for fine-grained control. The rules are simple: required parameters first, defaults evaluated once at definition time, and never use mutable objects as defaults. Master these rules and your function signatures will be both concise and safe.
More in this topic
Define and Call Python Functions
Learn the exact syntax for defining and calling Python functions with the def keyword, including naming rules, the function body, and how the call stack works.
Python Function Parameters and Arguments
Learn the difference between parameters and arguments, how to pass values by position and by keyword, and the rules for default parameter values.
Python Functions Explained
Learn what Python functions are, why they matter for organizing code, and how they form the foundation of non-trivial programs.