Courses/Python Mastery/Module 7: Functions
Module 7 · Lesson 215 minBeginner

Default & Keyword Arguments

Lesson goal
Make functions flexible without making them confusing.

Default & Keyword Arguments

Functions become *flexible* when parameters have defaults — callers fill what they care about and skip the rest. This is how libraries give you ten customization options without demanding ten arguments every time.

Default values

A parameter with = value becomes optional:

code
def greet(name, greeting="Hello"):
    return f"{greeting}, {name}!"

print(greet("Aarav"))                 # Hello, Aarav!      ← default used
print(greet("Aarav", "Namaste"))      # Namaste, Aarav!    ← override

One function, two behaviors. The caller decides how much to customize.

Multiple defaults — keyword arguments shine

code
def make_coffee(size="medium", milk=True, sugar=False, shots=1):
    order = f"{size} coffee"
    order += ", with milk" if milk else ", black"
    order += ", with sugar" if sugar else ""
    order += f", {shots} shot(s)"
    return order

print(make_coffee())                              # all defaults
print(make_coffee(sugar=True))                    # just flip one
print(make_coffee(size="large", shots=2))         # pick any two
print(make_coffee(milk=False, size="small"))      # any order!

Notice the last call: keyword arguments (name=value) can come in any order — the names do the matching. This is how you'll configure Streamlit widgets, API calls, and chart options for the rest of your coding life:

code
st.button("Save", key="save_btn")                    # key is a kwarg
requests.get(url, timeout=10, headers=headers)       # same idea

The one iron rule: positional before keyword

code
def introduce(name, age, city="Delhi"):
    ...

introduce("Aarav", 16)                  # ✅
introduce("Aarav", 16, city="Mumbai")   # ✅ positional then keyword
introduce(name="Aarav", 16)             # ❌ SyntaxError! keyword first

Python enforces the order: all positional arguments, then all keyword arguments.

The mutable default trap (famous interview question)

code
# ❌ BUGGY — do not do this
def add_item(item, cart=[]):        # the list is created ONCE!
    cart.append(item)
    return cart

print(add_item("milk"))     # ['milk']
print(add_item("eggs"))     # ['milk', 'eggs']  ← WAIT, why does it remember?!

Default values are created once, when the function is defined — not on each call. Both calls shared the *same list*. The fix:

code
# ✅ correct — default to None, create inside
def add_item(item, cart=None):
    if cart is None:
        cart = []
    cart.append(item)
    return cart

print(add_item("milk"))     # ['milk']
print(add_item("eggs"))     # ['eggs'] — fresh list each call

Memorize the shape: `param=None` + create inside. You'll see it in every professional codebase.

Defaults make APIs pleasant

Look at a real Streamlit widget signature (simplified):

code
st.slider("Height", min_value=100, max_value=250, value=170, step=1)

Five parameters, but you supply only what you need — the rest carry sensible defaults. That's the design goal for your own functions: required things first, optional things with good defaults after.

Common Errors & Fixes

  • `SyntaxError: non-default argument follows default argument` — a required parameter came after an optional one. Reorder: required first.
  • "My list remembers between calls" — the mutable default trap. param=None + create inside.
  • `TypeError: got multiple values for argument` — you passed a positional value for a parameter you also passed by keyword. Pick one style per parameter.

  • ✅ Checkpoint

  • Make power(base, exp) where exp defaults to 2. *(def power(base, exp=2): return base ** exp)*
  • Can keyword arguments appear before positional ones in a call? *(Never — positional first)*
  • Why does def f(items=[]) cause shared-state bugs? *(Default created once; all calls share it — use None + create inside)*
  • What makes library calls like st.slider(...) pleasant to use? *(Sensible defaults — supply only what you need)*
  • Next: *args and **kwargs — accepting "any number of anything."