Reputation: 3833
This seems like something that is likely to have been asked before, but an hour or so of searching has yielded no results. Passing default list argument to dataclasses looked promising, but it's not quite what I'm looking for.
Here's the problem: when one tries to assign a mutable value to a class attribute, there's an error:
@dataclass
class Foo:
bar: list = []
# ValueError: mutable default <class 'list'> for field a is not allowed: use default_factory
I gathered from the error message that I'm supposed to use the following instead:
from dataclasses import field
@dataclass
class Foo:
bar: list = field(default_factory=list)
But why are mutable defaults not allowed? Is it to enforce avoidance of the mutable default argument problem?
Upvotes: 251
Views: 168273
Reputation: 1093
I stumbled across this issue because I do want to have a static list as class variable. This can be done using the ClassVar
annotation:
from typing import ClassVar
@dataclass
class Foo:
bar: ClassVar[list[str]] = ['hello', 'world']
Upvotes: 11
Reputation: 3232
Just use a callable in your default_factory:
from dataclasses import dataclass, field
@dataclass
class SomeClass:
"""
"""
some_list: list = field(default_factory=lambda: ["your_values"])
If you want all instances to mutate the same list:
from dataclasses import dataclass, field
SHARED_LIST = ["your_values"]
@dataclass
class SomeClass:
"""
"""
some_list: list = field(default_factory=lambda: SHARED_LIST)
Upvotes: 26
Reputation: 435
import field like dataclass.
from dataclasses import dataclass, field
and use this for lists:
@dataclass
class Foo:
bar: list = field(default_factory=list)
Upvotes: 17
Reputation: 3833
It looks like my question was quite clearly answered in the docs (which derived from PEP 557, as shmee mentioned):
Python stores default member variable values in class attributes. Consider this example, not using dataclasses:
class C: x = [] def add(self, element): self.x.append(element) o1 = C() o2 = C() o1.add(1) o2.add(2) assert o1.x == [1, 2] assert o1.x is o2.x
Note that the two instances of class
C
share the same class variablex
, as expected.Using dataclasses, if this code was valid:
@dataclass class D: x: List = [] def add(self, element): self.x += element
it would generate code similar to:
class D: x = [] def __init__(self, x=x): self.x = x def add(self, element): self.x += element
This has the same issue as the original example using class
C
. That is, two instances of classD
that do not specify a value forx
when creating a class instance will share the same copy ofx
. Because dataclasses just use normal Python class creation they also share this behavior. There is no general way for Data Classes to detect this condition. Instead, dataclasses will raise aValueError
if it detects a default parameter of typelist
,dict
, orset
. This is a partial solution, but it does protect against many common errors.
Upvotes: 156