Default values, keyword-only parameters & the mutable default trap

Defaults and keyword arguments

Lesson 1 of 20 · 10 min · Module 1: Functions, deeper

Good functions are easy to call correctly. Default values and keyword arguments make calls readable, but one classic mistake makes defaults share data between calls.

Start this lesson →

Free. No installation: you write and run the code in your browser, and it is checked automatically.

By the end, you’ll be able to…

  • Use default values safely
  • Avoid shared mutable defaults
  • Require keyword arguments with *

The idea

A default is created once, when the function is defined. A default list such as tags=[] is therefore shared by every call. Use None and create a new list inside. A bare * in the parameter list makes the following parameters keyword-only: callers must write their names.

def greet(name, *, punctuation="!"):
    return "Hello, " + name + punctuation

print(greet("Mia", punctuation="?"))

01Defaults are evaluated once

Python evaluates def add(item, items=[]) one time. Every call that relies on the default receives that same list object, so items pile up across calls.

def bad(item, items=[]):
    items.append(item)
    return items

print(bad(1), bad(2))
Output
[1, 2] [1, 2]

02None means "not provided"

Using None as the default and creating the list inside the function gives each call a fresh list. The same pattern works for dicts and sets.

03Keyword-only parameters

Everything after a bare * must be named. add_tag("x", lower=False) is clear; add_tag("x", None, False) would hide what False means, so Python refuses it.

Your turn

Write add_tag(tag, tags=None, *, lower=True). It returns a list containing the existing tags plus tag (lowercased unless lower=False), without duplicates and without changing the list passed in. Calls without tags must never share a list.

What the checks look for

  • Calls without tags do not share a list
  • Keeps the caller’s list unchanged
  • Avoids duplicates and respects lower=False
  • lower can only be passed by keyword
Try it in the editor →

Take this with you

Use None for mutable defaults, and keyword-only parameters for options.

Go a little further

Add a keyword-only parameter limit that keeps only the last limit tags.

Learn Python Intermediate for free

20 short lessons with real code and instant checks.

See the whole course →