field() and default_factory
Overview
In Python `@dataclass` models, the `field()` function allows you to customize individual attributes. Its most important use is `default_factory`, which takes a zero-argument callable (like `list` or `dict`) to generate a fresh default value for each new instance. The mental model is 'factory vs. shared box': a standard default like `items = []` shares the exact same `list` across all instances. A `default_factory=list` calls `list()` to mint a brand-new box every time a new object is instantiated. If you try to use a mutable default directly, Python proactively stops you: @dataclass class Team: members: list = [] This raises a `ValueError`. This strict rule prevents the shared state bug, covered fully in `mutable-default-argument`.
It prevents the infamous mutable default argument bug in `@dataclass` by ensuring each instance gets its own independent mutable object.
Where used: Initializing `list` or `dict` fields in a `@dataclass`, Excluding fields from `__repr__`, Generating unique IDs on creation
Why learn this
- You can safely use `list`, `dict`, or `set` as default values in your data models.
- You can exclude sensitive fields (like passwords) from the automatically generated `__repr__`.
Code walkthrough
from dataclasses import dataclass, field
@dataclass
class Order:
items: list = field(default_factory=list)
o1 = Order()
o2 = Order()
o1.items.append('apple')
print(o2.items)
Focus: items: list = field(default_factory=list)
Aha moment
from dataclasses import dataclass, field
@dataclass
class Secret:
password: str = field(repr=False)
s = Secret('12345')
print(s)
Prediction: What does printing the dataclass output?
Common guess: Secret(password='12345')
It prints `Secret()`. Because we passed `repr=False` to the `field()` function, the `@dataclass` generator excludes the `password` from the automatically created `__repr__` method.
Common mistakes
- Calling the factory function: You must pass the function object itself to `default_factory`, not the result of calling it. Use `default_factory=list`, not `default_factory=list()`.
- Using default instead of default_factory for mutables: If you try to write `items: list = field(default=[])`, the `@dataclass` decorator will proactively raise a `ValueError` to protect you from the shared state bug.
Glossary
- callable
- Any Python object that can be called like a function, such as a regular function or a class, e.g. `callable(list)` is `True`.
- mutable
- An object whose internal state or data can be changed after it is created, like a `list` or a `dict`, e.g. `my_list.append(1)`.
Recall questions
- Why should you use `default_factory` instead of `default` for `list` or `dict` fields in a `@dataclass`?
- What kind of argument does `default_factory` expect?
Understanding checks
What does this code print?
99
When `User()` is instantiated without a `uid`, it calls the `get_id()` function passed to `default_factory` to generate the default value.
What happens when you define this class?
It raises a `ValueError`.
The `@dataclass` decorator detects the mutable default `list` `[]` and intentionally crashes to prevent the shared-state bug. It forces you to use `default_factory`.
Practice tasks
Add a dynamic default
Modify the `Group` dataclass so that `members` defaults to an empty `list`. You must use `field()` and `default_factory` to do this safely.
Challenge
Exclude from string representation
Create a `@dataclass` named `User` with `username` (`str`) and `token` (`str`). Use `field()` so that `token` does not appear when the object is printed.
Continue learning
Previous: @dataclass Basics
Previous: Mutable default argument pitfall