Python's Mutable Default Argument Trap: Why Your Function Remembers Too Much

June 05, 2026 1 min read 5 views
Minimalist illustration of a Python function definition on a dark editor screen with a subtle visual representation of a hidden trap in soft geometric style

You write a function, give it a list as a default argument, and everything seems fine. Then you call it a few times without passing that argument, and the results are wrong β€” the list is growing across calls as if it has a memory. You haven't introduced a global variable. You haven't done anything obviously wrong. But Python is quietly doing something that trips up nearly every developer the first time they see it.

This is the mutable default argument trap, and understanding it will change how you think about Python function definitions.

What you'll learn

  • Why Python evaluates default arguments once, not on every call
  • How to reproduce and diagnose the bug in your own code
  • The canonical fix using None as a sentinel value
  • When mutable defaults are actually intentional (and how to use them safely)
  • Common variants of this trap beyond just lists

Prerequisites

This article assumes you're comfortable writing Python functions and understand basic concepts like lists, dictionaries, and function signatures. No third-party libraries are needed β€” everything here runs in a plain Python 3 environment.

A Simple Reproduction

The fastest way to understand the trap is to see it in action. Here's the classic example:

def append_item(item, collection=[]):
    collection.append(item)
    return collection

print(append_item("apple"))   # ['apple']
print(append_item("banana"))  # ['apple', 'banana']  ← wait, what?
print(append_item("cherry"))  # ['apple', 'banana', 'cherry']  ← wrong

If you expected each call without a collection argument to start with an empty list, you're not alone. Most people do. But that's not what Python does.

Why This Happens: Default Arguments Are Evaluated Once

Python evaluates default argument expressions exactly once β€” when the def statement is executed, not when the function is called. That empty list [] is created a single time and stored as part of the function object itself.

You can see this directly by inspecting the function:

def append_item(item, collection=[]):
    collection.append(item)
    return collection

print(append_item.__defaults__)  # ([],)
append_item("apple")
print(append_item.__defaults__)  # (['apple'],)

The default value lives inside the function's __defaults__ tuple. When you mutate it inside the function body, you're mutating the same object that will be used as the default on every subsequent call. There's no reset mechanism.

This is not a bug in Python. It's a deliberate design decision consistent with Python's object model, where everything is an object and variables are references. The confusion comes from assuming that [] in a function signature means

πŸ“€ Share this article

Sign in to save

Comments (0)

No comments yet. Be the first!

Leave a Comment

Sign in to comment with your profile.

πŸ“¬ Weekly Newsletter

Stay ahead of the curve

Get the best programming tutorials, data analytics tips, and tool reviews delivered to your inbox every week.

No spam. Unsubscribe anytime.