Article Categories
- All Categories
-
Data Structure
-
Networking
-
RDBMS
-
Operating System
-
Java
-
MS Excel
-
iOS
-
HTML
-
CSS
-
Android
-
Python
-
C Programming
-
C++
-
C#
-
MongoDB
-
MySQL
-
Javascript
-
PHP
-
Economics & Finance
Why are default values shared between objects in Python?
Default values in Python are created only once when a function is defined, not each time the function is called. This behavior can cause unexpected issues when using mutable objects (like lists or dictionaries) as default parameters. Understanding this concept is crucial for writing reliable Python code.
The Problem with Mutable Defaults
When you use a mutable object as a default parameter, all function calls share the same object. Here's a demonstration of the problem:
def add_item(item, target_list=[]):
target_list.append(item)
return target_list
# First call
result1 = add_item("apple")
print("First call:", result1)
# Second call
result2 = add_item("banana")
print("Second call:", result2)
# Third call with explicit list
result3 = add_item("cherry", ["orange"])
print("Third call:", result3)
First call: ['apple'] Second call: ['apple', 'banana'] Third call: ['orange', 'cherry']
Notice how the second call contains both "apple" and "banana" because both calls shared the same default list object.
Why This Happens
Python evaluates default arguments only once at function definition time, not at each function call. The default value becomes part of the function object and persists between calls.
def problematic_function(data={}):
return data
# These all reference the same dictionary
call1 = problematic_function()
call2 = problematic_function()
print("Same object?", call1 is call2)
print("ID of call1:", id(call1))
print("ID of call2:", id(call2))
Same object? True ID of call1: 140234567890432 ID of call2: 140234567890432
The Solution: Use None as Default
The standard solution is to use None as the default value and create a new mutable object inside the function:
def add_item_fixed(item, target_list=None):
if target_list is None:
target_list = []
target_list.append(item)
return target_list
# First call
result1 = add_item_fixed("apple")
print("First call:", result1)
# Second call
result2 = add_item_fixed("banana")
print("Second call:", result2)
First call: ['apple'] Second call: ['banana']
Legitimate Use Case: Memoization
Sometimes the shared default behavior is actually useful, such as for caching expensive computations:
def fibonacci(n, _cache={}):
if n in _cache:
return _cache[n]
if n < 2:
result = n
else:
result = fibonacci(n-1) + fibonacci(n-2)
_cache[n] = result
return result
# Test the memoized function
print("fib(10):", fibonacci(10))
print("fib(15):", fibonacci(15))
print("Cache size:", len(fibonacci.__defaults__[0]))
fib(10): 55 fib(15): 610 Cache size: 16
Safe vs Unsafe Default Types
| Safe (Immutable) | Unsafe (Mutable) |
|---|---|
| None, int, float, str | list, dict, set |
| tuple, frozenset | class instances |
| bool | bytearray |
Conclusion
Always use None as the default value when you need a mutable object parameter. Create the actual mutable object inside the function to ensure each call gets a fresh instance. Only use mutable defaults intentionally for patterns like caching.
