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

时间:2018-12-05 12:17:02

标签: python python-3.x

这个似乎就像以前可能被询问过的东西,但是一个小时左右的搜索没有产生任何结果。 Passing default list argument to dataclasses看起来很有前途,但这并不是我想要的。

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

@dataclass
class Foo:
    bar: list = []

# ValueError: mutable default <class 'list'> for field a is not allowed: use default_factory

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

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

但是为什么不允许可变的默认值?是否要强制避免使用mutable default argument problem

2 个答案:

答案 0 :(得分:2)

我的问题似乎在the 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
     

请注意,类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
     

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

答案 1 :(得分:0)

鉴于@dataclass的动机之一是减少样板代码,然后在@dataclass的上下文中

x: list = [] 应该等于 x: list = field(default_factory=lambda: [])

仅仅是python像任何东西一样在进行中。

PEP中的示例不合理。应该是:

class D:
    def __init__(self, x=[]):
        self.x = x

那个 x = None的作品使整个“工厂”论证更加离奇。为什么无类型有效但列表类型无效?