为什么数据类的类属性声明中不能具有可变的默认值?

Gra*_*ham 15 python python-3.x

似乎喜欢的事,很可能已被问过,但搜索的一个小时左右的时间没有结果。将默认列表参数传递给数据类看起来很有希望,但这并不是我想要的。

这是问题所在:当尝试将可变值分配给class属性时,会出现错误:

@dataclass
class Foo:
    bar: list = []

# ValueError: mutable default <class 'list'> for field a is not allowed: use default_factory
Run Code Online (Sandbox Code Playgroud)

我从错误消息中收集了应该使用以下内容的信息:

@dataclass
class Foo:
    bar: list = field(default_factory=list)
Run Code Online (Sandbox Code Playgroud)

但是为什么不允许可变的默认值?是否要强制避免可变的默认参数问题

Shi*_*zzy 33

The above answer is not correct. A mutable default value, such as an empty list can be defined in data class by using default_factory.

    @dataclass
    class D:
        x: list = field(default_factory=list) 
Run Code Online (Sandbox Code Playgroud)

Using default factory functions is a way to create new instances of >mutable types as default values for fields:

   @dataclass
   class D:
       x: list = field(default_factory=list)

   assert D().x is not D().x
Run Code Online (Sandbox Code Playgroud)

The link is here

  • 您应该写“@用户名的答案”而不是“上述答案”,因为答案的显示顺序可能会随着时间的推移而改变。现在你的答案略低于你三周后写的答案。 (8认同)

Met*_*orm 11

只需在 default_factory 中使用可调用的:

from dataclasses import dataclass, field

@dataclass
class SomeClass:
    """
    """

    some_list: list = field(default_factory=lambda: ["your_values"])
Run Code Online (Sandbox Code Playgroud)

如果您希望所有实例都改变同一个列表:

from dataclasses import dataclass, field

SHARED_LIST = ["your_values"]
    
@dataclass
class SomeClass:
    """
    """
    
    some_list: list = field(default_factory=lambda: SHARED_LIST)
Run Code Online (Sandbox Code Playgroud)


Sad*_*yan 9

像数据类一样导入字段。

from dataclasses import dataclass, field
Run Code Online (Sandbox Code Playgroud)

并将其用于列表:

@dataclass
class Foo:
    bar: list = field(default_factory=list)
Run Code Online (Sandbox Code Playgroud)

  • 问题不在于写什么。问题是为什么不允许以显而易见的方式做事。 (3认同)

Gra*_*ham 8

看来我的问题在docs中很清楚地得到了回答(如shmee所述,它源自PEP 557):

Python将默认的成员变量值存储在类属性中。考虑以下示例,不使用数据类:

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
Run Code Online (Sandbox Code Playgroud)

请注意,class的两个实例按预期C共享相同的class变量x

如果此代码有效,则使用数据类:

@dataclass
class D:
    x: List = []
    def add(self, element):
        self.x += element
Run Code Online (Sandbox Code Playgroud)

它会生成类似于以下内容的代码:

class D:
    x = []
    def __init__(self, x=x):
        self.x = x
    def add(self, element):
        self.x += element
Run Code Online (Sandbox Code Playgroud)

这与使用class的原始示例具有相同的问题C。也就是说,创建类实例时D未指定值x的两个类实例将共享的相同副本x。因为数据类仅使用常规的Python类创建,所以它们也共享此行为。数据类没有检测这种情况的通用方法。相反,数据类将抛出一个TypeError,如果它检测类型的默认参数listdictset。这是部分解决方案,但确实可以防止许多常见错误。


Nic*_*ood 5

我偶然发现了这个问题,因为我确实想要一个静态列表作为类变量。这可以使用注释来完成ClassVar

from typing import ClassVar

@dataclass
class Foo:
    bar: ClassVar[list[str]] = ['hello', 'world']
Run Code Online (Sandbox Code Playgroud)