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")Rex is a dog.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")dog is a Rex.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)Making a medium coffee.
Making a large coffee.
Making a large coffee with an extra shot.- 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"))['apple']
['banana']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")Rex is a dog.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))Received: (1, 2, 3)
6
Received: (10, 20, 30, 40)
100The 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")Received: {'name': 'Maya', 'age': 17, 'city': 'Pune'}
name: Maya
age: 17
city: Pune*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)Customer: Riya
Items: ('book', 'pen')
Details: {'gift_wrap': True, 'express': False}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")Action: save
Args: (1, 2, 3)
Verbose: True
Kwargs: {'path': '/tmp'}- 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))60*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)Milo is a cat.**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
What is the difference between *args and **kwargs?
*args collects extra positional arguments into a tuple, while **kwargs collects extra keyword arguments (name=value pairs) into a dictionary.
Can I mix positional and keyword arguments in one call?
Yes, but all positional arguments must come before any keyword arguments in the call, for example, func(1, 2, key=3).
Why should I avoid mutable default values?
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.
Do I have to name the variable args or kwargs?
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.
Can I unpack a list and a dictionary into the same call?
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.