LearnContact
Lesson 2430 min read

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.

What You Will Learn
  • 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.

positional_args.py
def describe_pet(species, name):
    print(f"{name} is a {species}.")

describe_pet("dog", "Rex")
Output
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.

What happens if order is swapped
def describe_pet(species, name):
    print(f"{name} is a {species}.")

describe_pet("Rex", "dog")
Output
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.

default_values.py
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)
Output
Making a medium coffee.
Making a large coffee.
Making a large coffee with an extra shot.
Mutable Default Trap
  • 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.
Safe pattern for mutable defaults
def add_item(item, cart=None):
    if cart is None:
        cart = []
    cart.append(item)
    return cart

print(add_item("apple"))
print(add_item("banana"))
Output
['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.

keyword_arguments.py
def describe_pet(species, name):
    print(f"{name} is a {species}.")

describe_pet(name="Rex", species="dog")
Output
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.

star_args.py
def total(*args):
    print("Received:", args)
    return sum(args)

print(total(1, 2, 3))
print(total(10, 20, 30, 40))
Output
Received: (1, 2, 3)
6
Received: (10, 20, 30, 40)
100

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.

double_star_kwargs.py
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")
Output
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.

Combining everything
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)
Output
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.

PositionParameter TypeExample
1Regular / default parametersdef f(a, b=1)
2*argsdef f(a, *args)
3Keyword-only parameters (after *args)def f(a, *args, mode)
4**kwargsdef f(a, *args, mode, **kwargs)
correct_parameter_order.py
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")
Output
Action: save
Args: (1, 2, 3)
Verbose: True
Kwargs: {'path': '/tmp'}
Invalid Order Raises a SyntaxError
  • 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.

unpacking_a_list.py
def add(a, b, c):
    return a + b + c

values = [10, 20, 30]
print(add(*values))
Output
60

*values unpacks the list [10, 20, 30] into three separate positional arguments, exactly as if you had written add(10, 20, 30).

unpacking_a_dict.py
def describe_pet(species, name):
    print(f"{name} is a {species}.")

pet_info = {"species": "cat", "name": "Milo"}
describe_pet(**pet_info)
Output
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

Avoid These 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.

Lesson 24 Completed
  • 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.
Next Lesson →

Variable Scope