Fun with Learning Technology
LearnCoursesQuestionsTracksToolsNewsExplorePractice
Fun with Learning Technology

A new problem, explained clearly, every day.

Subscribe
Learn
  • Lessons
  • Topics
  • News
  • Tools
  • Courses
  • Career tracks
  • Everything
Site
  • About
  • Contact
  • Support
  • Privacy
  • Terms
Get the daily one

One email per new problem. No spam.

Request a tutorial

Requests shape what gets made next.

© 2026 Fun with Learning TechnologyRSS
Home›Courses›Python›Default and Keyword Arguments

Functions

Default and Keyword Arguments

Default arguments allow functions to have pre-configured values, while keyword arguments enhance readability by explicitly mapping values to parameter names. Understanding these mechanisms is crucial for creating flexible, self-documenting APIs that handle diverse inputs gracefully. Mastering these tools ensures you can design functions that remain compatible with evolving requirements without forcing repetitive calls.

Understanding Default Arguments

Default arguments are defined in the function signature by assigning a value to a parameter using the assignment operator. When a function is called, if the caller does not provide an argument for that parameter, the function automatically uses the defined default value. This mechanism is foundational because it reduces the amount of code repetition required when a function is commonly called with the same settings. The key to understanding this is realizing that the default value is calculated and bound at the exact moment the function is defined, not when it is executed. Consequently, if you use a mutable object as a default, that object is shared across all subsequent calls, which often leads to unintended side effects if the object is modified. By providing sensible defaults, you make your utility functions significantly easier to use, as the user only needs to specify non-standard behavior.

def connect_to_database(host='localhost', port=5432):
    # The defaults are set at definition time.
    print(f"Connecting to {host} on port {port}")

connect_to_database() # Uses defaults
connect_to_database('production.db', 8080) # Overrides both

Positional vs Keyword Arguments

In Python, arguments can be passed to functions either positionally or by keyword. Positional arguments are assigned based on the order in which they appear in the function call, which relies heavily on the developer remembering the parameter sequence. Keyword arguments allow you to pass values by explicitly stating the parameter name, such as 'parameter_name=value'. This approach decouples the call from the parameter order, making the code significantly more readable and robust against changes in function signature. When mixing both, positional arguments must always come before keyword arguments. If you rely purely on positional arguments for functions with many parameters, your code becomes fragile, as swapping the order of parameters in the signature will break all existing function calls. Using keyword arguments makes the intent of the caller clear, as the function call itself acts as documentation, mapping inputs to their specific roles.

def process_payment(amount, currency='USD', store='online'):
    # Explicit keywords make logic clear at the call site.
    print(f"Processing {amount} {currency} for {store}")

process_payment(100, store='retail') # Mixing positional and keyword

The Mutable Default Argument Pitfall

A common trap involves using mutable objects, such as lists or dictionaries, as default arguments. Because the default value is assigned once at definition time, the same object instance is reused for every call to the function that lacks a specific argument. If your function modifies this mutable object, those changes persist across subsequent calls, leading to bugs that are notoriously difficult to track down. This behavior occurs because the identifier for the default argument points to a specific memory location initialized when the function was created. To avoid this, developers should follow the standard pattern of using 'None' as the default value and initializing the mutable object inside the function body. This ensures that a brand-new object is instantiated every time the function is called without a specific input, preserving the integrity of the data and preventing state pollution between independent execution cycles.

def add_item(item, basket=None):
    # Never use [] as a default; use None instead.
    if basket is None:
        basket = []
    basket.append(item)
    return basket

print(add_item('apple')) # ['apple']
print(add_item('orange')) # ['orange']

Enforcing Keyword-Only Arguments

Sometimes it is necessary to force a caller to specify arguments by name to avoid confusion, especially when a function takes several boolean flags or similar parameters. You can enforce this by placing an asterisk (*) in the function signature. Any parameters appearing after the asterisk cannot be passed positionally; they must be provided as keyword arguments. This is an advanced technique that improves the clarity of complex API surfaces. When a developer encounters a function with many inputs, keyword-only arguments prevent them from passing values in the wrong sequence, which might cause logic errors that are hard to detect at runtime. By requiring explicit naming, you force the developer to consider what each argument represents, which inherently improves code quality and maintainability in large-scale projects. This effectively creates a self-documenting interface where ambiguity is minimized or eliminated entirely during the interaction with your code.

def configure_server(*, timeout=30, retry=True):
    # The * forces keyword-only usage for security/clarity.
    print(f"Timeout set to {timeout}, Retry: {retry}")

configure_server(timeout=60) # Success
# configure_server(60) # This would raise a TypeError

Advanced Argument Unpacking

Python provides a powerful way to handle dynamic arguments using dictionary unpacking with the double asterisk operator (**). When you define a function with **kwargs, it collects any extra keyword arguments passed by the caller into a dictionary. This allows your functions to accept a variable number of parameters, making them highly versatile when you do not know the exact arguments beforehand. It is essential to combine this with default values correctly to ensure the logic remains predictable. By leveraging dictionary unpacking, you can write wrapper functions that pass settings through to other internal processes or use data structures to construct function calls dynamically. This capability is used extensively in library development to provide extensible APIs that can accommodate future changes without breaking existing calls. It effectively bridges the gap between static function definitions and the dynamic requirements of complex, data-driven applications in production environments.

def setup_system(**settings):
    # Collects arbitrary keyword arguments into a dict.
    defaults = {'theme': 'dark', 'verbose': False}
    defaults.update(settings)
    print(f"System initialized: {defaults}")

setup_system(theme='light', log_level=1) # Dynamic configuration

Key points

  • Default arguments are evaluated only once at the time the function is defined.
  • Using mutable objects as default arguments causes state to persist across multiple function calls.
  • Keyword arguments allow parameters to be passed in any order by specifying their names.
  • Positional arguments must always precede keyword arguments in a function call.
  • The None pattern is the standard way to safely initialize mutable default parameters.
  • The asterisk in a signature forces subsequent arguments to be passed as keywords only.
  • Double asterisks collect arbitrary keyword arguments into a dictionary for dynamic handling.
  • Always use keyword arguments for functions with multiple boolean parameters to improve call site readability.

Common mistakes

  • Mistake: Using mutable objects like lists or dictionaries as default arguments. Why it's wrong: Default arguments are evaluated only once at definition time, so the same object persists across multiple function calls. Fix: Use 'None' as the default value and initialize the object inside the function body.
  • Mistake: Misunderstanding the order of positional and keyword arguments. Why it's wrong: Python requires positional arguments to appear before keyword arguments in a function call. Fix: Ensure all positional arguments are placed before any 'key=value' keyword assignments.
  • Mistake: Attempting to use a non-default argument after a default argument in the function definition. Why it's wrong: Python syntax rules forbid 'non-default argument follows default argument' because it creates ambiguity. Fix: Place all arguments with default values after those without defaults.
  • Mistake: Overwriting keyword arguments by providing a positional argument that matches the keyword name. Why it's wrong: If you pass a value positionally that is already mapped to a keyword name, it raises a TypeError. Fix: Keep your function signatures clean and avoid positional-keyword overlaps.
  • Mistake: Expecting default arguments to be re-evaluated on every function call. Why it's wrong: New developers often think 'def func(x=[])' creates a new list every time, but it actually creates one list object at definition. Fix: Initialize mutable structures inside the function to ensure a fresh instance every time.

Interview questions

What is a default argument in Python and how do you define one?

A default argument is a parameter that is assigned a default value in the function definition. If the caller does not provide an argument for that parameter, the function uses the default value. You define one using the syntax 'def function(param=value):'. This is useful for making functions flexible, allowing users to specify only the parameters they wish to change while keeping common settings as standard defaults.

What are keyword arguments and why would you use them instead of positional arguments?

Keyword arguments allow you to pass arguments to a function by explicitly stating the parameter name, such as 'func(name='Alice', age=30)'. They are superior to positional arguments because they improve code readability and remove the need to remember the exact order of parameters. Furthermore, if a function has many parameters, using keywords makes the function call self-documenting, ensuring that developers know exactly which value corresponds to which variable.

What happens if you use a mutable object like a list as a default argument in Python?

Using a mutable object, such as a list or dictionary, as a default argument is a common pitfall because the default value is evaluated only once at the time the function is defined, not every time the function is called. If you modify the list inside the function, those changes persist across subsequent calls. For example, 'def append_to(item, target=[])' will cause the target list to grow every time the function is invoked without a second argument.

How can you prevent the accidental persistence of mutable default arguments?

The best practice to avoid the persistence issue is to set the default argument to 'None' and then initialize the mutable object inside the function body. For example: 'def process(data=None): if data is None: data = []'. This ensures that a new instance of the list or dictionary is created fresh every time the function is executed, maintaining the expected behavior and preventing unintended side effects across multiple calls.

Compare using *args and **kwargs with standard keyword arguments. When would you prefer one over the other?

Standard keyword arguments are used when the function signature is fixed and the parameters are known in advance. In contrast, *args and **kwargs are used to create flexible functions that accept an arbitrary number of arguments. Use *args and **kwargs when building wrappers, decorators, or APIs where you do not know the exact number of inputs beforehand. Standard keyword arguments are preferred for API clarity and validation.

What is the purpose of 'keyword-only' arguments, and how do you enforce them in a Python function?

Keyword-only arguments are parameters that must be passed using the keyword syntax, preventing them from being passed positionally. You enforce this by placing an asterisk '*' in the function signature before the keyword-only parameters. For example, 'def func(a, *, b):' requires 'b' to be called as 'func(1, b=2)'. This pattern is extremely powerful when designing APIs where you want to ensure clear intent for specific, perhaps less obvious, configuration flags.

All Python interview questions →

Check yourself

1. What is the output of this code? def append_to(element, to=[]): to.append(element) return to print(append_to(1)) print(append_to(2))

  • A.[1], [2]
  • B.[1], [1, 2]
  • C.[1], [2, 1]
  • D.Error: Invalid default argument
Show answer

B. [1], [1, 2]
The list is created at definition time. Option 1 is wrong because it assumes the list is created per call. Option 2 is correct because the same list is mutated. Option 3 assumes order reversal. Option 4 is wrong because mutable defaults are allowed, just discouraged.

2. Which of the following function definitions is syntactically invalid?

  • A.def func(a, b=10): pass
  • B.def func(a=5, b=10): pass
  • C.def func(a=5, b): pass
  • D.def func(a, b=None): pass
Show answer

C. def func(a=5, b): pass
In Python, mandatory positional arguments cannot follow arguments with default values. Option 1, 2, and 4 are valid because they respect the positional-then-default order.

3. How can you safely use a list as a default argument to ensure a new list is created each time?

  • A.def func(data=list()): pass
  • B.def func(data=None): data = data or []
  • C.def func(data=[]): data = list(data)
  • D.def func(data=None): if data is None: data = []
Show answer

D. def func(data=None): if data is None: data = []
Option 3 is the standard idiomatic way to handle mutable defaults safely. Option 1 uses a function call at definition, which doesn't solve the state persistence. Option 2 behaves differently if data is an empty list (evaluates to false). Option 4 correctly checks for None.

4. Given `def func(a, b=2):`, which of these is an INCORRECT way to call the function?

  • A.func(1, 3)
  • B.func(a=1, b=3)
  • C.func(1, b=3)
  • D.func(b=3, 1)
Show answer

D. func(b=3, 1)
Python requires positional arguments to come before keyword arguments. Option 3 is incorrect because '1' is positional and comes after 'b=3', which is a keyword argument.

5. When are default argument values evaluated in Python?

  • A.Every time the function is called
  • B.Only once, when the function definition is executed
  • C.When the script finishes execution
  • D.Only if the argument is omitted by the caller
Show answer

B. Only once, when the function definition is executed
Default arguments are bound at the moment the 'def' statement is executed. Option 1 is the most common misconception. Option 3 is irrelevant. Option 4 describes when they are used, not when they are evaluated.

Take the full Python quiz →

← PreviousFunction Arguments and ParametersNext →Return Values and Multiple Returns

Python

78 lessons, free to read.

All lessons →

Track your progress

Sign in to mark lessons done, score quizzes and keep notes.

Open in the app