Free demoJob-oriented courses at Debugshala, Old Palasia, Indore. Freshers, non-IT and year-gap welcome.Book a free demo +91-8982385539

Python Function Arguments: Positional, Keyword and Default

Lesson 26 of 52 · 9 min · Updated 13 Nov 2026

Learn Python function arguments: positional vs keyword, default values, the mutable default trap, keyword-only and positional-only parameters, with examples.

Picture a tailor's shop near Khajrana that takes orders for kurtas. The order slip has boxes for customer name, chest size, length, fabric and delivery date. Most customers just say their name and measurements in the usual order.

Some prefer to say "fabric cotton, length 42" in whatever order comes to mind. Delivery date is left blank by most people, in which case the shop writes "7 days" by default.

Those three habits, filling boxes in order, naming the box you mean, and leaving some boxes to their usual value, are exactly how Python passes values into a function.

Filling in order is a positional argument. Saying "fabric = cotton" is a keyword argument. The pre-printed "7 days" is a default value. The order slip itself, with its labelled boxes, is the function's parameter list.

Function arguments in Python are the values passed into a function call, matched to the function's parameters either by position or by keyword, with default values filling in any optional parameters left out.

The previous lesson, Python functions, covered def and return. This one is about the brackets: every way to get values in, and the rules Python enforces while matching them. All examples run on Python 3.12+.

Positional vs keyword arguments

Positional arguments are matched to parameters left to right. Keyword arguments are matched by name, so their order does not matter. You can mix both in one call, as long as every positional argument comes before the first keyword argument.

Python
def invoice_line(product, qty, price):
    return f"{product:<10} {qty:>3} x {price:>5} = {qty * price:>6}"

print(invoice_line("Kurta", 3, 899))                       # positional
print(invoice_line(price=1499, product="Sherwani", qty=1)) # keyword, any order
print(invoice_line("Dupatta", price=349, qty=4))           # mixed

The one ordering rule Python will not bend is that positional arguments come first. Put a positional value after a keyword one and Python refuses to run the file at all, before a single line executes:

Output
invoice_line(product="Kurta", 3, 899)
SyntaxError: positional argument follows keyword argument

Other mismatches only show up when the call actually runs, as a TypeError. This block triggers four of them on purpose.

Python
def invoice_line(product, qty, price):
    return f"{product} x{qty} = {qty * price}"

bad_calls = [
    lambda: invoice_line("Kurta", 3),                  # missing price
    lambda: invoice_line("Kurta", 3, 899, 10),         # one too many
    lambda: invoice_line("Kurta", 3, 899, qty=2),      # qty given twice
    lambda: invoice_line("Kurta", 3, cost=899),        # no such parameter
]
for call in bad_calls:
    try:
        call()
    except TypeError as err:
        print("TypeError:", err)

Each message names the problem precisely, so read it before guessing. The tiny lambda: wrappers just delay each call so the loop can try them one by one; Python lambda explains them.

How Python matches positional and keyword arguments to function parameters, with a default filling the missing value

Positional values fill parameters from the left, keyword values jump to their named slot, and defaults fill whatever is left.

When should you use keyword arguments?

Use keywords whenever the meaning of a value is not obvious from the value itself. report(2026, 11, True, False) tells a reader nothing. report(year=2026, month=11, include_returns=True, send_email=False) reads like a sentence and survives someone reordering the parameters later.

How do default argument values work?

Give a parameter a value in the definition, such as gst=0.18, and callers may leave it out. Parameters with defaults must come after those without, because Python fills positional values left to right.

Python
def final_price(amount, gst=0.18, discount=0):
    return round(amount * (1 + gst) - discount)

print(final_price(1000))                    # both defaults
print(final_price(1000, 0.05))              # gst given by position
print(final_price(1000, discount=100))      # skip gst, set discount
print(final_price(1000, gst=0, discount=50))

Keyword arguments let you skip over a default in the middle, as the third call does. Without them, you would have to repeat 0.18 just to reach discount.

When are default values evaluated?

Exactly once, when the def statement runs, not on every call. For numbers, strings and None this makes no difference. For a list or dictionary it causes one of Python's best-known bugs.

Quick check

def price(amount, gst=0.18, discount=0). Which call sets discount to 50 and keeps the default gst?

The mutable default argument trap

Suppose you write a helper that adds a tag to a customer's tag list, creating a new list when none is passed.

Python
def add_tag(tag, tags=[]):           # BUG: one list shared by every call
    tags.append(tag)
    return tags

print(add_tag("premium"))
print(add_tag("emi"))                # expected ['emi']
print(add_tag("repeat"))

def add_tag_fixed(tag, tags=None):   # FIX: create a new list per call
    if tags is None:
        tags = []
    tags.append(tag)
    return tags

print(add_tag_fixed("premium"))
print(add_tag_fixed("emi"))

The buggy version keeps growing: ['premium'], then ['premium', 'emi'], then all three. The empty list was created once, at definition time, and every call that relies on the default appends to that same list.

The Python mutable default argument trap: one list object created at def time is shared by every call, versus a None default creating a new list each call

A list default lives on the function object and is reused; a None default lets each call build its own list.

The fix is a standard idiom: default to None, then create the list inside the function. Use the same pattern for dictionaries and sets. Many linters flag def f(x=[]) for exactly this reason.

Arguments are references to objects

Python does not copy arguments. The parameter becomes a new name for the same object the caller passed. What happens next depends on whether the object can change.

Python
def apply_discount(prices, pct):
    for i in range(len(prices)):
        prices[i] = round(prices[i] * (1 - pct / 100))
    return prices

def apply_discount_safe(prices, pct):
    return [round(p * (1 - pct / 100)) for p in prices]

shop = [1000, 2500, 400]
new = apply_discount_safe(shop, 10)
print("safe:", shop, new)

apply_discount(shop, 10)
print("in place:", shop)            # the caller's list changed

city = "indore"
def shout(s):
    s = s.upper()                   # rebinding: only the local name changes
    return s
print(shout(city), city)

Changing a mutable object in place, such as setting prices[i], is visible to the caller. Rebinding the parameter to a new object, as s = s.upper() does, is not. For analysis code, returning a new list is usually safer than editing the caller's data in place. The lists article explains why two names can share one list.

Keyword-only and positional-only parameters

Two special markers in a parameter list let you control how callers may pass values.

Marker Meaning Example definition Valid call
* Parameters after it must be passed by keyword def export(df, *, sep=",") export(data, sep=";")
/ Parameters before it must be passed by position def ratio(a, b, /) ratio(3, 4)
both Mix of the two def f(a, /, b, *, c) f(1, 2, c=3) or f(1, b=2, c=3)
Python
def monthly_report(month, year, *, include_returns=False, currency="INR"):
    flag = "with returns" if include_returns else "net of returns"
    return f"{month}/{year} report, {flag}, in {currency}"

print(monthly_report(11, 2026, include_returns=True))
try:
    monthly_report(11, 2026, True)               # flag passed by position
except TypeError as err:
    print("TypeError:", err)

def growth(old, new, /):
    return round((new - old) / old * 100, 1)

print(growth(180000, 205500))

Keyword-only flags are a gift to future readers: nobody can write a mysterious True in a call any more. Positional-only parameters, written with / since Python 3.8, are common in built-ins such as len(obj, /). They let a library rename a parameter later without breaking callers.

What about the * in *args? A bare * collects nothing; it is just a marker. A named *args collects any extra positional values into a tuple, which is the topic of the next lesson, Python *args and **kwargs.

Quick check

In def send(to, *, subject), how must subject be passed?

Common mistakes with function arguments

  • Mutable defaults. def f(items=[]) shares one list across calls. Default to None.
  • Positional after keyword. f(a=1, 2) is a SyntaxError. Put positional values first.
  • Non-default before default in the definition. def f(a=1, b) is a SyntaxError.
  • Passing the same parameter twice. f(1, a=2) where a is the first parameter raises TypeError.
  • Unreadable boolean flags. report(True, False) hides intent. Make flags keyword-only.
  • Silently changing the caller's list. Return a new list unless in-place editing is the documented purpose.

Key takeaways

  • Positional arguments match by order; keyword arguments match by name.
  • In a call, all positional arguments come before keyword arguments.
  • Defaults make parameters optional and are evaluated once, at def time.
  • Use None as the default for lists, dicts and sets, and create them inside.
  • Functions receive references, so in-place changes to mutable arguments affect the caller.
  • * makes later parameters keyword-only; / makes earlier ones positional-only.
Take the free Python Programming quiz10 quick questions with explanations. No sign-up needed, and you can come back to this lesson after.

FAQ

What are the types of function arguments in Python?

Python has positional arguments, matched by order; keyword arguments, matched by name; and default arguments, which are optional because the definition supplies a value. Functions can also collect any number of extra arguments with *args and **kwargs, and use * and / to require keyword-only or positional-only passing.

What is the difference between positional and keyword arguments?

Positional arguments are assigned to parameters in the order they appear in the call. Keyword arguments name the parameter, as in qty=3, so their order does not matter. Keyword arguments make calls clearer and let you skip defaults, but they must come after all positional arguments.

Why should you not use a list as a default argument in Python?

Python evaluates default values once, when the function is defined, so a list default is a single object shared by every call. Appending to it in one call leaves the item there for the next call. Use None as the default and create a new list inside the function instead.

Does Python pass arguments by value or by reference?

Neither exactly. Python passes references to objects, often called "pass by object reference". The function can change a mutable object, such as a list, and the caller will see it. Rebinding the parameter to a new object inside the function does not affect the caller's variable.

Is there a classroom Python course for fresh graduates in Indore?

Yes. Debugshala runs data analytics batches for fresh graduates at its Old Palasia campus in Indore, starting with Python fundamentals. Classes are in English and Hindi, a free demo comes first, and you can check your grasp of arguments on Debugshala Test.

From the team behind this site

Go further with the Data Analytics Course at Debugshala

These tutorials are written by Debugshala, a training and placement institute at Old Palasia, Indore. The classroom course adds live mentoring, real projects and interview preparation.

  • Offline classes at Old Palasia, Indore
  • Free demo class before you pay
  • Non-IT, fresher and year-gap friendly
  • Placement assistance

700+ students placed · 200+ hiring partners · 4.9★ Google rating

Python Functions: def, return, Docstrings and Reusable CodePython *args and **kwargs: Packing, Unpacking and Forwarding