Python Default, Keyword, and Variable Arguments: A Practical Guide

Python Default, Keyword, and Variable Arguments: A Practical Guide


Functions become powerful when you control how values are passed into them. In this beginner-friendly Python function parameters tutorial, you’ll learn python default keyword and variable arguments with clear examples, common mistakes to avoid, and simple rules you can remember. If you are just starting with Python, you can browse more basics in our Python tutorials at CodDesire.

What are function parameters and arguments?

Illustration of Python default arguments: missing parameters use defaults, provided values override them.
How Python uses default values when you skip or override parameters.

Parameters are the names you put in a function definition; arguments are the actual values you pass when calling the function.

def area(width, height): # width, height are parameters
return width * height

print(area(3, 4)) # 3 and 4 are arguments
Output
12

Python lets you pass arguments by position, by name (keyword arguments), and it also supports default values and “variable length” arguments using *args and **kwargs. Understanding these tools helps you write cleaner, safer, and more flexible functions.

Key takeaways — python default keyword and variable arguments
  • Default values are evaluated once (at definition time). Avoid mutable defaults; use None and create the object inside.
  • Call order matters: positional args first, then keyword args. No parameter may receive two values.
  • Control your API: use / for positional-only and * for keyword-only parameters.
  • *args collects extra positional args (tuple); **kwargs collects extra keyword args (dict with string keys only).
  • Prefer keyword arguments for optional settings; force keyword-only for clarity and safer future changes.

Python default arguments explained

Visualizing python default keyword and variable arguments: positional, *args, keywords, and **kwargs flow.
Where positional args, *args, keywords, and **kwargs go when a Python function is called.

A default argument is a parameter with a value that is used if the caller doesn’t provide one. This makes parameters optional and keeps your calls simple.

Code
def greet(name="World"):
    return f"Hello, {name}!"

print(greet())
print(greet("Aisha"))
Output
Hello, World!
Hello, Aisha

Key rule: default values are evaluated once—when the def statement runs—not every time the function is called. That’s powerful, but it also leads to a classic beginner pitfall.

Common mistake: default mutable arguments cause bugs

Using a mutable object (like a list or dict) as a default can unexpectedly remember data between calls.

def add_item(item, basket=[]): # Don't do this!
basket.append(item)
return basket

print(add_item("apple"))
print(add_item("banana")) # Oops! The previous "apple" sticks around
Output
['apple']
['apple', 'banana']

Fix it by using None as a sentinel and creating a new object inside the function.

Code
def add_item_safe(item, basket=None):
    if basket is None:
        basket = []
    basket.append(item)
    return basket

print(add_item_safe("apple"))
print(add_item_safe("banana"))
Output
['apple']
['banana']

Use this pattern for any mutable default (lists, dicts, sets). This is one of the most common mistakes with default mutable arguments in Python—and now you know how to avoid it.

Python keyword arguments for beginners

Keyword arguments let you pass values by parameter name. This improves readability and makes calls more flexible, especially when functions have many optional parameters.

Code
def enroll(name, course, level="beginner"):
    return f"{name} - {course} ({level})"

# Positional arguments
print(enroll("Ravi", "Python"))

# Keyword arguments
print(enroll(name="Ravi", course="Python", level="intermediate"))

# Mix: positional first, then keyword
print(enroll("Ravi", course="Python", level="advanced"))
Output
Ravi - Python (beginner)
Ravi - Python (intermediate)
Ravi - Python (advanced)
  • Positional arguments must come first; keyword arguments follow.
  • You cannot give the same parameter two values (e.g., both as positional and as keyword, or duplicated via multiple ** dicts).
Code
def f(x, y=0):
    return x + y

# Duplicate "x" via two **dicts causes a runtime TypeError
try:
    print(f(**{"x": 1}, **{"x": 2}))
except TypeError as e:
    print("TypeError:", e)
Output
TypeError: f() got multiple values for argument 'x'

When should I choose keyword arguments over positional arguments?

  • When readability matters: resize(width=800, height=600) is clearer than resize(800, 600) to someone reading the code later.
  • When many parameters are optional: call the ones you care about by name and skip the rest.
  • When you want a stable API: if parameter order changes later, keyword calls remain correct.

Positional-only and keyword-only parameters

Python lets you make intent explicit:

  • Positional-only parameters: must be given by position. Use / in the function signature.
  • Keyword-only parameters: must be given by name. Introduce them after * (or after *args).
def move(x, y, /, *, speed=1.0):
return f"Moving to ({x}, {y}) at speed {speed}"

print(move(10, 20)) # OK
print(move(3, 4, speed=2.5)) # OK
# x=..., y=... would be invalid because x and y are positional-only
Output
Moving to (10, 20) at speed 1.0
Moving to (3, 4) at speed 2.5

Real-world example: print uses keyword-only parameters for options like sep and end. Keyword-only parameters make “options” explicit and self-documenting.

How Python binds call arguments to parameters (default, keyword, and variable)
1
Fill positional-only parameters (before /) from the left using positional arguments.

2
Use remaining positional args to fill positional-or-keyword parameters (before *).

3
If present, collect any extra positional args into *args.

4
Bind keyword arguments by name to any unfilled parameters. Each parameter can get a value only once.

5
Fill keyword-only parameters (after *) from the remaining keywords. Missing required ones cause TypeError.

6
If present, collect any extra keyword args into **kwargs (keys must be strings).

7
Validate: no duplicates, no missing required params, and no unexpected keywords; otherwise raise TypeError.

Python variable length arguments: *args and **kwargs

Sometimes you don’t know how many arguments you’ll get. That’s where “variable arguments” help:

  • *args collects extra positional arguments into a tuple.
  • **kwargs collects extra keyword arguments into a new dict (keys must be strings).
Code
def product(*numbers):
    result = 1
    for n in numbers:
        result *= n
    return result

print(product())
print(product(2, 3, 4))

def make_profile(name, **info):
    profile = {"name": name}
    profile.update(info)
    return profile

print(make_profile("Lia", age=19, city="Delhi"))
Output
1
24
{'name': 'Lia', 'age': 19, 'city': 'Delhi'}

Keys in a **kwargs mapping must be strings. If not, Python raises a TypeError at call time.

def show(**kw):
print(kw)

try:
show(**{1: "x"}) # keys must be strings
except TypeError as e:
print("TypeError:", e)
Output
TypeError: keywords must be strings

When to use *args and **kwargs in Python

  • To accept a flexible number of inputs (e.g., sum-like utilities).
  • To forward arguments to another function in wrappers, decorators, and adapters.
  • To future-proof APIs when you may add more options later (use keyword-only or **kwargs thoughtfully).

Forwarding arguments with *args and **kwargs

Code
def debug_call(fn, *args, **kwargs):
    print("Calling:", fn.__name__, "args=", args, "kwargs=", kwargs)
    return fn(*args, **kwargs)

def area(width, height=1, *, unit="px"):
    return f"{width * height}{unit}^2"

print(debug_call(area, 3, height=5, unit="cm"))
Output
Calling: area args= (3,) kwargs= {'height': 5, 'unit': 'cm'}
15cm^2

Unpacking with * and ** in function calls

You can split a sequence into positional arguments with * and a mapping into keyword arguments with **. Python lets you combine multiple * and ** in the same call.

Code
def product(*numbers):
    result = 1
    for n in numbers:
        result *= n
    return result

nums = [1, 2, 3]
more = (4, 5)
print(product(*nums, *more))  # both unpacked

def make_profile(name, **info):
    out = {"name": name}
    out.update(info)
    return out

options1 = {"age": 20}
options2 = {"city": "Pune"}
print(make_profile("Jay", **options1, **options2))
Output
120
{'name': 'Jay', 'age': 20, 'city': 'Pune'}
  • If two ** mappings contain the same parameter name, the call raises TypeError (“multiple values for argument”).
  • Positional arguments (and * expansions) must come before keyword arguments (and ** expansions).

Quick comparison of argument kinds

Kind How to declare How to pass Notes
Positional-only Before / (e.g., def f(x, /):) By position only Good for stable APIs where names are internal
Positional-or-keyword Normal parameters (before *) By position or by name Most common parameter kind
Var-positional *args Collects extra positional args Forms a tuple
Keyword-only After * or after *args By name only Great for options like sep, end in print
Var-keyword **kwargs Collects extra keyword args Forms a dict; keys must be strings

Putting it together: small practice

Try a function that uses a variable number of positional arguments and a keyword-only option for formatting.

Code
def average(*numbers, decimals=2):
    if not numbers:
        return 0.0
    mean = sum(numbers) / len(numbers)
    return round(mean, decimals)

print(average(10, 20, 30))
print(average(1, 2, 3, 4, decimals=3))
Output
20.0
2.500

Tips for clean, stable function APIs

  • Use clear defaults for the most common cases and document them.
  • Avoid mutable defaults; use None + in-function initialization.
  • Prefer keyword arguments for optional “tuning” parameters.
  • Use keyword-only parameters to force clarity for options.
  • Use positional-only parameters to keep names private/stable if you don’t want callers to rely on them.
  • Forward arguments in wrappers with *args and **kwargs to avoid breaking changes.
Practical checklist — python default keyword and variable arguments
  • Defaults: never use mutable defaults (lists, dicts, sets). Use None then create inside.
  • Signature clarity: add / for positional-only inputs and * to start keyword-only options.
  • Calling rules: place positional args (and * unpacking) before keywords (and ** unpacking).
  • Forwarding: wrappers should accept *args/**kwargs and pass them through unchanged.
  • Options: make optional settings keyword-only for readability and future compatibility.
  • Validation: raise clear errors for unknown keywords or missing required params.
  • Docs: show at least one call using keywords and one using unpacking (*/**).

FAQ – Default, Keyword, and Variable Arguments

What are default arguments in Python and how do they work?

They are parameters with preset values used when the caller omits them. Defaults are evaluated once at function definition time, then reused on every call. This is why using mutable defaults can cause state to “stick.”

How do keyword arguments differ from positional arguments in Python?

Positional arguments are matched by their order. Keyword arguments use the parameter name (name=value). Keyword arguments improve readability and let you pass only the options you care about, but they must come after all positional arguments in a call.

What are *args and **kwargs used for in Python functions?

*args collects any extra positional arguments into a tuple; **kwargs collects any extra keyword arguments into a dict. They’re perfect for flexible functions, wrappers, and forward-compatibility.

Why can default mutable arguments cause bugs in Python?

Because the default object is created once and then reused. If you mutate it (like appending to a list), future calls see the mutated object. Use None as a default and create a new object inside the function.

When should I choose keyword arguments over positional arguments?

Use keyword arguments for clarity, for optional settings, and for APIs you want to keep stable even if you later change parameter order. Reserve positional arguments for the essential, obvious parameters.

Further examples: special parameters in practice

Here’s a small function that combines multiple ideas: positional-only required values, keyword-only options, and safe defaults.

Code
def export(data, /, *, fmt="json", indent=2):
    if fmt == "json":
        class="cd-package">import json
        return json.dumps(data, indent=indent)
    elif fmt == "csv":
        # Minimal CSV for simple data
        lines = []
        for row in data:
            lines.append(",".join(map(str, row)))
        return "n".join(lines)
    else:
        raise ValueError("Unsupported format")

print(export([["name", "score"], ["Aisha", 95]], fmt="csv"))
Output
name,score
Aisha,95

Note how data is positional-only (callers can’t rely on the parameter name), while fmt and indent are keyword-only options.

Sources / Further reading

Wrap-up

You’ve learned how to use default arguments, when to reach for keyword arguments, and how to capture flexible inputs with *args and **kwargs. Mastering python default keyword and variable arguments lets you design functions that are clean, readable, and friendly to both beginners and future you. Keep exploring and practicing in our Python section at CodDesire.

Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted