270

This is similar to Passing default list argument to dataclasses, 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:

from dataclasses import dataclass @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 dataclass, 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?

5
  • 16
    "Is it to enforce avoidance of the mutable default argument problem" Yes. Imagine a change to one instance changing all of instances ever created. If this is one's desired behavior they should use a class attribute. Commented Dec 5, 2018 at 12:21
  • 2
    Relevant section of PEP 557 explaining this design. Commented Dec 5, 2018 at 13:08
  • 5
    Your question answered my question, clearly you are smarter than me. Take this upvote! Commented Dec 11, 2019 at 10:27
  • I think this youtrack.jetbrains.com/issue/PY-42319 Commented Feb 11, 2021 at 9:04
  • 13
    As I've still managed to miss this solution in the question, I'll copy the proper syntax here: bar: list = dataclasses.field(default_factory=list) Commented Jun 19, 2021 at 13:28

4 Answers 4

171

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 variable x, 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 class D that do not specify a value for x when creating a class instance will share the same copy of x. 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 a ValueError if it detects a default parameter of type list, dict, or set. This is a partial solution, but it does protect against many common errors.

Sign up to request clarification or add additional context in comments.

Comments

36

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) 

1 Comment

This defeats the purpose of @dataclass, doesn't it ? It's meant to represent a collection of individual entities or values. If you implement the equivalent of a class attribute, it's probably better to create an usual class.
23

import field like dataclass.

from dataclasses import dataclass, field 

and use this for lists:

@dataclass class Foo: bar: list = field(default_factory=list) 

2 Comments

The question is not about what to write instead. The question is about why it is not permitted to do things in the obvious way.
I actually looked for solutions to this issue, and found here :)
13

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'] 

1 Comment

Best solution. By the way, you don't even need to annotate Foo as @dataclass.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.