💡 Python QuickBits - 🛡️ Safer Defaults with default_factory
Posted On: August 27, 2025
Description:
When working with dataclasses in Python, a common pitfall is using mutable objects like [] or {} as field defaults. In older Python versions, this caused all instances of a class to share the same list or dict.
From Python 3.11 onwards, this isn’t even allowed — you’ll get a ValueError telling you to use default_factory instead.
The Wrong Way: Mutable Defaults
from dataclasses import dataclass
from typing import List
@dataclass
class BadTagStore:
tags: List[str] = [] # ❌ BAD: raises ValueError in Python 3.11+
⛔ In Python 3.11+, this immediately raises:
ValueError: mutable default <class 'list'> for field tags is not allowed: use default_factory
In older versions (<3.11), this silently worked but caused shared mutable state across instances.
The Fix: default_factory
The correct way is to use field(default_factory=...), which creates a fresh object for every instance:
from dataclasses import dataclass, field
from typing import List
@dataclass
class GoodTagStore:
tags: List[str] = field(default_factory=list) # ✅ GOOD
a = GoodTagStore()
b = GoodTagStore()
a.tags.append("python")
print("a:", a.tags) # ['python']
print("b:", b.tags) # [] — independent
Practical Variations
default_factory works not just with lists, but also with dicts, sets, and even custom functions:
from dataclasses import dataclass, field
from typing import Dict, List
@dataclass
class ConfigStore:
config: Dict[str, str] = field(default_factory=dict)
features: set = field(default_factory=set)
defaults: List[str] = field(default_factory=lambda: ["default"])
Every instance of ConfigStore gets its own independent defaults.
Key Points
- Don’t use [] or {} as dataclass defaults.
- Use field(default_factory=...).
- Python 3.11+ enforces this best practice with a ValueError.
- Works for any mutable type: list, dict, set, or custom factories.
Code Snippet:
from dataclasses import dataclass, field # dataclass utilities and safe default factory
from typing import List, Dict # type hints for clarity
from dataclasses import dataclass
from typing import List
try:
@dataclass
class BadTagStore:
tags: List[str] = [] # ❌ BAD: raises ValueError in Python 3.11+
except ValueError as ve:
print(f"ValueError - {str(ve)}")
# Running this in Python 3.11+ will throw:
# ValueError: mutable default for field tags is not allowed: use default_factory
@dataclass # safer dataclass
class GoodTagStore: # corrected class
tags: List[str] = field(default_factory=list) # ✅ GOOD: a new list each time
good1 = GoodTagStore() # first safe instance
good2 = GoodTagStore() # second safe instance
good1.tags.append("python") # mutate tags on first instance
print("Good store1:", good1.tags) # ['python']
print("Good store2:", good2.tags) # [] (independent)
print("Same object?", good1.tags is good2.tags) # False
@dataclass
class ConfigStore:
# dict gets a fresh copy per instance
config: Dict[str, str] = field(default_factory=dict)
# set gets a fresh copy per instance
features: set = field(default_factory=set)
# even a custom factory function
def_list: List[str] = field(default_factory=lambda: ["default"])
c1 = ConfigStore()
c2 = ConfigStore()
c1.config["mode"] = "dark"
print("Config1:", c1.config) # {'mode': 'dark'}
print("Config2:", c2.config) # {} — independent
print("Features1:", c1.features) # set()
print("Default list2:", c2.def_list) # ['default']
No comments yet. Be the first to comment!