Default and Keyword Arguments
Explore how default and keyword arguments enhance function flexibility in Python. Learn to set optional parameters with defaults, call functions using named arguments to improve readability, and understand rules for mixing argument types. Understand potential pitfalls with mutable defaults and how to avoid common bugs by using None as a safe default. This lesson helps you write more adaptable and clear Python functions.
In earlier lessons, we worked with functions that required a fixed number of arguments, provided in a specific order. While this is useful for learning the basics, real-world programs rarely operate under such strict conditions. Data is often incomplete, optional, or variable.
Consider a function that creates a user profile. A username may be required, but details like an address or profile picture maybe optional. Forcing callers to supply every possible value makes functions harder to use and easier to misuse.
By learning default arguments and keyword arguments, we can write flexible functions. These features allow functions to adapt to different inputs while maintaining clear and predictable behavior.
Default arguments: Making parameters optional
When defining a function, we often want some parameters to have reasonable default values that are used unless the caller explicitly provides a different one. These are known as default arguments. We create them by assigning a value to a parameter directly in the function definition using the assignment operator (=).
When the function is called:
If the caller provides a value for that parameter, the function uses the provided argument.
If the caller omits the argument, the function automatically falls back to the default value.
When defining functions, Python enforces a strict rule, "required parameters (non-defaults) must be defined before optional parameters (defaults)." If we place an optional parameter before a required one, the Python interpreter can not reliably map positional arguments to the required parameters.
Line 1: We define
create_profilewith two parameters.roleis assigned a default value of"Subscriber".Lines 3–5: The function prints the provided username and role.
Line 8: We call the function passing only
"jdoe_99". Python assigns this tousernameand uses the default value,"Subscriber", forrole.Line 11: Next, we call the function with two arguments. This time
"Administrator"overrides the default value.
Keyword arguments: Calling functions flexibly
So far, we have passed arguments by position: the first value goes to the first parameter, the second to the second, and so on. However, as functions grow complex, remembering the exact order of five or six parameters becomes difficult and error-prone.
Python allows us to use keyword arguments in our function calls. We explicitly name the parameter we are setting (e.g., name="value"). This improves readability because the code clearly states what each value represents. It also frees us from strict ordering; if we use keywords, we can pass arguments in any order we like.
Lines 1–3: We define a function requiring three parameters:
pet_name,animal_type, andage.Line 6: We call the function positionally. We must remember that "Luna" comes before "cat", and "cat" comes before 4.
Line 9: Here, we call the function using keywords. Even though
ageis the last parameter in the definition, we can pass it first here. Python automatically matches values to the correct variable names.
The rules of mixing argument types
We can mix positional and keyword arguments in a single call, but Python enforces a strict rule that the positional arguments must always come before keyword arguments. This rule exists to prevent ambiguity. If we provided a keyword argument first, and then a positional one, Python wouldn't know which remaining parameter the positional value belongs to.
Try running the code below. It contains a deliberate error.
Line 6: This line fails because the positional argument
5comes after the keyword argumenttax=0.08.
How to resolve? To fix the error in the code above, replace line 6 with this valid call where the positional argument comes first: calculate_total(100, tax=0.08, discount=5)
The mutable default trap
There is one important detail regarding default arguments that catches almost every Python beginner. It stems from how Python handles function definitions in memory.
Problem: Python evaluates default argument values only once, when the function is defined, not every time the function is called.
If you use a mutable object (like a list [] or dictionary {}) as a default value, Python creates that object exactly once. Every single time you call the function without providing that argument, Python recycles the same list.
If Call A adds an item to the list, that item stays there.
When Call B runs, it sees the item added from Call A.
This creates "phantom" data that leaks from one function call to the next, causing bugs that are very hard to track down.
To avoid this, we never use mutable objects as defaults. Instead, we use None as the default value. Inside the function, we check for None and create a fresh list. This ensures every call gets its own brand-new empty list.
Line 2: We set
class_listto default toNone(which is immutable) instead of[].Lines 4–5: We check if
class_listisNone. If it is, we create a fresh, empty list. This happens during the call, guaranteeing a new list every time.Line 10: Calling the function creates a new list containing only "Alice".
Line 11: Calling it again creates a fresh list containing only "Bob". If we had used
class_list=[], "Bob" would have been added to Alice's list!
By using default and keyword arguments, we shift the burden of complexity from the caller to the function author. We can create functions that have sensible defaults for standard behavior but remain completely customizable for advanced scenarios. We also make our code significantly more readable by allowing explicit parameter naming. However, we must respect the ordering rules: positional before keyword in calls, and required before default in definitions to keep our syntax valid.