Function Arguments
Master every way Python lets you pass arguments into a function, including defaults, keyword arguments, *args, and **kwargs.
Introduction
In the previous lesson you defined simple functions with plain parameters. Python actually offers several flexible ways to pass arguments into a function — default values, named keyword arguments, and even a variable number of arguments using *args and **kwargs.
Understanding these tools lets you write functions that are flexible and convenient to call, which is why nearly every real Python library — from print() itself to major frameworks — relies heavily on them.
- Positional arguments and how order matters.
- Default parameter values for optional arguments.
- Keyword arguments for clarity and flexibility.
- *args for accepting any number of positional values.
- **kwargs for accepting any number of named values.
- The required order of parameter types and how to unpack collections into a call.
Positional Arguments
Positional arguments are matched to parameters purely by their order — the first argument fills the first parameter, the second fills the second, and so on.
def describe_pet(species, name): print(f"{name} is a {species}.")
describe_pet("dog", "Rex")Click Run to see what this code prints.
Here "dog" fills species and "Rex" fills name because of their position. Swapping the order of the arguments would swap their meaning too, which can cause subtle bugs.
def describe_pet(species, name): print(f"{name} is a {species}.")
describe_pet("Rex", "dog")Click Run to see what this code prints.
Default Parameter Values
You can give a parameter a default value by writing = value after its name in the function definition. If the caller does not supply that argument, the default is used automatically.
def make_coffee(size="medium", extra_shot=False): shot_text = " with an extra shot" if extra_shot else "" print(f"Making a {size} coffee{shot_text}.")
make_coffee()make_coffee("large")make_coffee("large", True)Click Run to see what this code prints.
- Never use a mutable object like [] or {} as a default value.
- Defaults are created only once, when the function is defined — not each call.
- A mutable default gets shared and modified across every call, causing confusing bugs.
- Use None as the default instead, and create the list/dict inside the function body.
def add_item(item, cart=None): if cart is None: cart = [] cart.append(item) return cart
print(add_item("apple"))print(add_item("banana"))Click Run to see what this code prints.
Keyword Arguments
A keyword argument is passed using name=value at the call site, matching the parameter by name instead of position. This makes calls clearer and lets you supply arguments in any order.
def describe_pet(species, name): print(f"{name} is a {species}.")
describe_pet(name="Rex", species="dog")Click Run to see what this code prints.
Even though name is written first at the call site, Python matches each value by name, so the output is still correct regardless of order.
*args: Variable Positional Arguments
Sometimes you do not know in advance how many positional values a function should accept. Prefixing a parameter with a single asterisk (*args) collects any number of extra positional arguments into a tuple.
def total(*args): print("Received:", args) return sum(args)
print(total(1, 2, 3))print(total(10, 20, 30, 40))Click Run to see what this code prints.
The name args is just a convention — what matters is the single asterisk (*), which tells Python to gather every extra positional argument into a tuple.
**kwargs: Variable Keyword Arguments
Similarly, prefixing a parameter with two asterisks (**kwargs) collects any number of extra keyword arguments into a dictionary, where each key is the argument name and each value is what was passed.
def build_profile(**kwargs): print("Received:", kwargs) for key, value in kwargs.items(): print(f"{key}: {value}")
build_profile(name="Maya", age=17, city="Pune")Click Run to see what this code prints.
*args
Collects extra positional arguments into a tuple.
**kwargs
Collects extra keyword arguments into a dictionary.
Combined
A function can accept both regular parameters, *args, and **kwargs together.
def order_summary(customer, *items, **details): print("Customer:", customer) print("Items:", items) print("Details:", details)
order_summary("Riya", "book", "pen", gift_wrap=True, express=False)Click Run to see what this code prints.
Required Parameter Order
When a function definition mixes several parameter types, Python enforces a strict order: standard positional/default parameters first, then *args, then any keyword-only parameters, then **kwargs last.
| Position | Parameter Type | Example |
|---|---|---|
| 1 | Regular / default parameters | def f(a, b=1) |
| 2 | *args | def f(a, *args) |
| 3 | Keyword-only parameters (after *args) | def f(a, *args, mode) |
| 4 | **kwargs | def f(a, *args, mode, **kwargs) |
def process(action, *args, verbose=False, **kwargs): print("Action:", action) print("Args:", args) print("Verbose:", verbose) print("Kwargs:", kwargs)
process("save", 1, 2, 3, verbose=True, path="/tmp")Click Run to see what this code prints.
- You cannot place a regular parameter after **kwargs.
- You cannot place *args after **kwargs.
- Python checks this order at definition time, before the function is ever called.
Unpacking Into a Call
The * and ** operators also work at the call site, in reverse: they unpack an existing list or dictionary into separate arguments automatically.
def add(a, b, c): return a + b + c
values = [10, 20, 30]print(add(*values))Click Run to see what this code prints.
*values unpacks the list [10, 20, 30] into three separate positional arguments, exactly as if you had written add(10, 20, 30).
def describe_pet(species, name): print(f"{name} is a {species}.")
pet_info = {"species": "cat", "name": "Milo"}describe_pet(**pet_info)Click Run to see what this code prints.
**pet_info unpacks the dictionary into keyword arguments, matching species and name by key. The dictionary's keys must match the function's parameter names exactly.
Common Mistakes
- Using a mutable object like [] or {} as a default parameter value.
- Mixing up * (tuple of positional args) with ** (dictionary of keyword args).
- Placing a regular parameter after *args or **kwargs in the definition.
- Passing a positional argument after a keyword argument at the call site, which raises a SyntaxError.
- Assuming *args and **kwargs must be named exactly that — the asterisks matter, not the names.
Best Practices
- Use keyword arguments for parameters whose meaning is not obvious from position alone.
- Give optional parameters sensible default values instead of forcing every caller to supply them.
- Reach for *args and **kwargs only when the number of arguments truly varies.
- Use None as a default placeholder for mutable defaults, and build the object inside the function.
- Keep function signatures as simple as possible — do not add *args/**kwargs just because you can.
Frequently Asked Questions
*args collects extra positional arguments into a tuple, while **kwargs collects extra keyword arguments (name=value pairs) into a dictionary.
Yes, but all positional arguments must come before any keyword arguments in the call, for example, func(1, 2, key=3).
Default values are created only once when the function is defined. A mutable default like a list gets reused and modified across every call that relies on it, causing unexpected shared state.
No. The names args and kwargs are just a strong convention. What actually matters to Python is the single * or double ** prefix before the parameter name.
Yes. For example, func(*my_list, **my_dict) unpacks positional values from the list and keyword values from the dictionary in the same call.
Key Takeaways
- Positional arguments are matched to parameters by order.
- Default parameter values make an argument optional when calling a function.
- Keyword arguments are matched by name and can be passed in any order.
- *args collects extra positional arguments into a tuple.
- **kwargs collects extra keyword arguments into a dictionary.
- Parameter order must follow: regular/default, then *args, then keyword-only, then **kwargs.
- The * and ** operators can also unpack a list or dict into a function call.
Summary
Python offers a rich set of tools for passing arguments: plain positional arguments, default values for optional parameters, named keyword arguments, and the flexible *args/**kwargs for accepting a variable number of values.
Mastering these patterns lets you design functions that are both powerful and pleasant to call. Next, you will learn how Python decides which variables a function can actually see — variable scope.
- You understand positional, default, and keyword arguments.
- You can use *args and **kwargs to accept variable numbers of arguments.
- You know the required order of parameter types in a definition.
- You can unpack a list or dictionary into a function call.
- You are ready to learn about variable scope.