Course topics

By WebNest Studio

Python Tutorial

Function Arguments in Python

Python functions have one of the most flexible parameter systems of any language: positional and keyword arguments, default values, variable numbers of arguments with *args and **kwargs, keyword-only and positional-only parameters, and argument unpacking at the call site.

This lesson covers every kind of parameter, the order they must appear in, the mutable-default-argument trap, and how to design clear function signatures.

Positional, Keyword and Default Arguments

Arguments can be passed by position (area(3, 4)) or by name (area(width=3, height=4)), and keyword arguments can come in any order. Parameters with default values (def greet(name, greeting="Hello")) become optional. Parameters with defaults must come after those without.

*args and **kwargs

*args collects extra positional arguments into a tuple; **kwargs collects extra keyword arguments into a dict. They let functions accept any number of values and are used for wrappers and decorators that pass arguments through. The names are conventions — only the stars matter.

Keyword-Only and Positional-Only Parameters

Parameters after * (or after *args) are keyword-only: callers must name them, which makes calls like send(email, retry=True) self-documenting. Parameters before / are positional-only: callers cannot use their names, which lets you rename them later. The full order is: positional-only, /, normal, *args or *, keyword-only, **kwargs.

Unpacking and the Mutable Default Trap

At the call site, *sequence spreads items as positional arguments and **mapping spreads a dict as keyword arguments. Default values are evaluated once, when the function is defined — so a default like items=[] is shared between calls. Use None as the default and create the list inside the function.

Examples

Positional, keyword and default arguments

Python
def order_summary(item, quantity=1, price=100.0, currency="INR"):
    return f"{quantity} x {item} = {quantity * price:.2f} {currency}"

print(order_summary("pen"))
print(order_summary("book", 3, 250))
print(order_summary("bag", price=899, quantity=2))
print(order_summary(currency="USD", item="mug", price=9.5))
Output
1 x pen = 100.00 INR
3 x book = 750.00 INR
2 x bag = 1798.00 INR
1 x mug = 9.50 USD

*args and **kwargs

Python
def total(*numbers):
    print("numbers is a", type(numbers).__name__, numbers)
    return sum(numbers)

def create_user(username, **details):
    print("details is a", type(details).__name__, details)
    return {"username": username, **details}

print(total(10, 20, 30))
print(create_user("asha", city="Pune", age=24))

def log_call(func, *args, **kwargs):
    print(f"calling {func.__name__} with {args} {kwargs}")
    return func(*args, **kwargs)

print(log_call(round, 3.14159, ndigits=2))
Output
numbers is a tuple (10, 20, 30)
60
details is a dict {'city': 'Pune', 'age': 24}
{'username': 'asha', 'city': 'Pune', 'age': 24}
calling round with (3.14159,) {'ndigits': 2}
3.14

Keyword-only and positional-only parameters

Python
def send_email(to, subject, /, *, cc=None, urgent=False):
    return f"to={to} subject={subject} cc={cc} urgent={urgent}"

print(send_email("a@x.in", "Hi", urgent=True))

try:
    send_email("a@x.in", "Hi", None, True)
except TypeError as e:
    print("TypeError:", e)

try:
    send_email(to="a@x.in", subject="Hi")
except TypeError as e:
    print("TypeError:", e)
Output
to=a@x.in subject=Hi cc=None urgent=True
TypeError: send_email() takes 2 positional arguments but 4 were given
TypeError: send_email() got some positional-only arguments passed as keyword arguments: 'to, subject'

Argument unpacking and the mutable default trap

Python
def point(x, y, z=0):
    return (x, y, z)

coords = [1, 2, 3]
options = {"x": 5, "y": 6}
print(point(*coords), point(**options))

def add_item_bad(item, cart=[]):
    cart.append(item)
    return cart

def add_item_good(item, cart=None):
    if cart is None:
        cart = []
    cart.append(item)
    return cart

print(add_item_bad("pen"), add_item_bad("book"))
print(add_item_good("pen"), add_item_good("book"))
Output
(1, 2, 3) (5, 6, 0)
['pen', 'book'] ['pen', 'book']
['pen'] ['book']

Common Mistakes

  • Using a mutable default argument like [] or {}.
  • Putting a parameter without a default after one with a default (SyntaxError).
  • Passing boolean flags positionally (send(x, True, False)) — make them keyword-only.
  • Overusing **kwargs so that nobody can tell what a function accepts.

Key Points to Remember

  • Arguments can be positional or keyword; defaults make parameters optional.
  • *args collects extra positionals (tuple); **kwargs extra keywords (dict).
  • Parameters after * are keyword-only; before / are positional-only.
  • *seq and **dict unpack arguments at the call site.
  • Use None, not [] or {}, as a default for mutable values.

Practice the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.