提问人:Graham 提问时间:12/5/2018 最后编辑:colllinGraham 更新时间:9/2/2023 访问量:105077
为什么数据类的类属性声明中不能有可变的默认值?
Why can't dataclasses have mutable defaults in their class attributes declaration?
问:
这似乎是以前可能被问过的问题,但一个小时左右的搜索没有结果。将默认列表参数传递给数据类看起来很有希望,但这并不完全是我想要的。
问题来了:当尝试为类属性分配可变值时,会出现错误:
@dataclass
class Foo:
bar: list = []
# ValueError: mutable default <class 'list'> for field a is not allowed: use default_factory
我从错误消息中收集到我应该改用以下内容:
from dataclasses import field
@dataclass
class Foo:
bar: list = field(default_factory=list)
但是为什么不允许可变默认值呢?是为了强制避免可变的默认参数问题吗?
答:
看起来我的问题在文档中得到了非常清楚的回答(正如 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
请注意,class 的两个实例共享相同的类变量 ,正如预期的那样。
C
x
使用数据类,如果此代码有效:
@dataclass class D: x: List = [] def add(self, element): self.x += element
它将生成类似于以下内容的代码:
class D: x = [] def __init__(self, x=x): self.x = x def add(self, element): self.x += element
这与使用 class 的原始示例存在相同的问题。也就是说,在创建类实例时未指定值的两个类实例将共享 的同一副本。由于数据类仅使用普通的 Python 类创建,因此它们也共享此行为。数据类没有通用的方法来检测这种情况。相反,如果数据类检测到 、 或 类型的默认参数,则它将引发 。这是一个部分解决方案,但它确实可以防止许多常见错误。
C
D
x
x
ValueError
list
dict
set
以上答案不正确。可变默认值(如空列表)可以在数据类中使用 定义。default_factory
@dataclass
class D:
x: list = field(default_factory=list)
使用默认工厂函数是创建>可变类型的新实例作为字段默认值的一种方式:
@dataclass class D: x: list = field(default_factory=list) assert D().x is not D().x
链接在这里
评论
导入字段,如 dataclass。
from dataclasses import dataclass, field
并将其用于列表:
@dataclass
class Foo:
bar: list = field(default_factory=list)
评论
只需在default_factory中使用可调用对象:
from dataclasses import dataclass, field
@dataclass
class SomeClass:
"""
"""
some_list: list = field(default_factory=lambda: ["your_values"])
如果您希望所有实例都改变同一个列表:
from dataclasses import dataclass, field
SHARED_LIST = ["your_values"]
@dataclass
class SomeClass:
"""
"""
some_list: list = field(default_factory=lambda: SHARED_LIST)
我偶然发现了这个问题,因为我确实希望有一个静态列表作为类变量。这可以使用 ClassVar
注解来完成:
from typing import ClassVar
@dataclass
class Foo:
bar: ClassVar[list[str]] = ['hello', 'world']
评论
bar: list = dataclasses.field(default_factory=list)