Python dataclasses.dataclass 引用变量而不是实例变量

Tal*_*Tal 3 python instance-variables class-variables

c1 和 c2 的构造函数中的默认值应该为 b 和 b 生成新的实例变量。相反,看起来 c1.a 和 c2.a 正在引用相同的变量。@dataclass 是否创建了一个类变量?这似乎与预期的功能不一致,我在文档中找不到关于类变量的任何信息。所以,我认为这是一个错误。有人可以向我解释如何修复它吗?我应该将其报告为 python 跟踪器上的错误吗?

我知道这个问题必须与 python 按引用传递对象和按值传递内置类型的方式有关,因为 b 属性(它只是一个浮点数)显示了预期/期望的行为,而 a 属性(这是一个用户定义的对象)只是一个引用。

谢谢!

从数据类导入数据类

"""输入"""

@dataclass
class VS:
    v: float  # value
    s: float  # scale factor
    
    def scaled_value(self):
        return self.v*self.s

@dataclass
class Container:
    a: VS = VS(1, 1)
    b: float = 1

c1 = Container()
c2 = Container()

print(c1)
print(c2)

c1.a.v = -999
c1.b = -999

print(c1)
print(c2)
Run Code Online (Sandbox Code Playgroud)

"""输出"""

Container(a=VS(v=1, s=1), b=1)
Container(a=VS(v=1, s=1), b=1)
Container(a=VS(v=-999, s=1), b=-999)
Container(a=VS(v=-999, s=1), b=1)
Run Code Online (Sandbox Code Playgroud)

Cal*_*leb 5

在 OP 的原始示例中,定义类VS时会创建一个对象Container。然后该对象在Container类的所有实例之间共享。这是一个问题,因为用户定义的类(如VS导致可变对象)。因此,a任何Container对象的改变都会改变a所有其他Container对象

每次Container在初始化时实例化类时,您都希望生成一个新的 VS 对象。要做到这一点,使用default_factory的的field功能是去了解它的好方法。传递 lambda 函数允许所有这些都内联完成。

我将一个c成员变量添加到另一个VS类的容器中,以说明在这样做时成员是独立的。

from dataclasses import dataclass, field

@dataclass
class VS:
    v: float  # value
    s: float  # scale factor
    
    def scaled_value(self):
        return self.v*self.s

# Use a zero-argument lambda function for default factor function.      
@dataclass
class Container:
    a: VS = field(default_factory= lambda:VS(1,1) )
    b: float = 1
    c: VS = field(default_factory= lambda:VS(1,2) )

c1 = Container()
c2 = Container()

print(c1)
print(c2)

c1.a.v = -999
c1.c.s = -999

print(c1)
print(c2)
Run Code Online (Sandbox Code Playgroud)

输出:

Container(a=VS(v=1, s=1), b=1, c=VS(v=1, s=2))
Container(a=VS(v=1, s=1), b=1, c=VS(v=1, s=2))
Container(a=VS(v=-999, s=1), b=1, c=VS(v=1, s=-999))
Container(a=VS(v=1, s=1), b=1, c=VS(v=1, s=2))
Run Code Online (Sandbox Code Playgroud)