Python dataclasses.dataclass 引用变量而不是实例变量
Python dataclasses.dataclass reference to variable instead of instance variable
c1 和 c2 的构造函数中的默认值应该为 b 和 b 生成新的实例变量。相反,c1.a 和 c2.a 似乎引用了同一个变量。 @dataclass 是在创建一个 class 变量吗?这似乎与预期的功能不一致,我在文档中找不到任何关于 class 变量的信息。所以,我认为这是一个错误。有人可以向我解释如何解决吗?我应该将其报告为 python 跟踪器上的错误吗?
我知道这个问题一定与 python 通过引用传递对象和通过值传递内置类型的方式有关,因为 b 属性(只是一个浮点数)显示了 expected/desired 行为而 a 属性(这是一个用户定义的对象)只是一个参考。
谢谢!
从数据classes导入数据class
"""输入"""
@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)
"""输出"""
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)
感谢 Eric S 提供解释:
"""
c1 和 c2 共享 a 的同一个实例。这是可变默认参数问题:https://docs.python-guide.org/writing/gotchas/#mutable-default-arguments
使用default_factory为每个容器创建一个新的VS。
"""
default_factory 不允许我为多个属性设置一组唯一的默认 VS 值,因为需要在 VS 数据类中定义 VS 默认值。例如,如果我希望 a 默认为 VS(1,1) 但我希望 b 默认为 VS(1,2),default_factory 对我没有帮助。因此,我找到了解决方法,即创建关键字条目的字典并将深度复制传递到我的 Container() 构造函数中(请注意,如果我不传递深度复制,我会遇到与上述相同的问题)。这是我的最终代码片段和输出:
"""代码"""
@dataclass
class VS:
v: float = 1 # value
s: float = 1 # scale factor
def scaled_value(self):
return self.v*self.s
@dataclass
class Container:
a: VS = field(default_factory=VS)
b: float = 1
ip = {'a':VS(2,1),'b':1}
c1 = Container(**deepcopy(ip))
c2 = Container(**deepcopy(ip))
print(c1)
print(c2)
c1.a.v = 0
c1.b = 0
print(c1)
print(c2)
"""输出"""
容器(a=VS(v=2, s=1), b=1)
容器(a=VS(v=2, s=1), b=1)
容器(a=VS(v=0, s=1), b=0)
容器(a=VS(v=2, s=1), b=1)
在 OP 的原始示例中,当定义 Container
class 时,将创建一个 VS
对象。然后该对象在 Container
class 的所有实例之间共享。这是一个问题,因为 user-defined class 等 VS
会导致可变对象。因此,更改任何 Container
对象中的 a
将更改所有其他 Container
对象中的 a
你想在每次 Container
class 在初始化时实例化时生成一个新的 VS 对象。为此,使用 field
函数的 default_factory
是一个很好的方法。传递 lambda 函数允许所有这些内联完成。
我在容器中添加了一个 c
成员变量和另一个 VS
class 来说明这样做时成员是独立的。
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)
输出:
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))
c1 和 c2 的构造函数中的默认值应该为 b 和 b 生成新的实例变量。相反,c1.a 和 c2.a 似乎引用了同一个变量。 @dataclass 是在创建一个 class 变量吗?这似乎与预期的功能不一致,我在文档中找不到任何关于 class 变量的信息。所以,我认为这是一个错误。有人可以向我解释如何解决吗?我应该将其报告为 python 跟踪器上的错误吗?
我知道这个问题一定与 python 通过引用传递对象和通过值传递内置类型的方式有关,因为 b 属性(只是一个浮点数)显示了 expected/desired 行为而 a 属性(这是一个用户定义的对象)只是一个参考。
谢谢!
从数据classes导入数据class
"""输入"""
@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)
"""输出"""
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)
感谢 Eric S 提供解释:
""" c1 和 c2 共享 a 的同一个实例。这是可变默认参数问题:https://docs.python-guide.org/writing/gotchas/#mutable-default-arguments
使用default_factory为每个容器创建一个新的VS。 """
default_factory 不允许我为多个属性设置一组唯一的默认 VS 值,因为需要在 VS 数据类中定义 VS 默认值。例如,如果我希望 a 默认为 VS(1,1) 但我希望 b 默认为 VS(1,2),default_factory 对我没有帮助。因此,我找到了解决方法,即创建关键字条目的字典并将深度复制传递到我的 Container() 构造函数中(请注意,如果我不传递深度复制,我会遇到与上述相同的问题)。这是我的最终代码片段和输出:
"""代码"""
@dataclass
class VS:
v: float = 1 # value
s: float = 1 # scale factor
def scaled_value(self):
return self.v*self.s
@dataclass
class Container:
a: VS = field(default_factory=VS)
b: float = 1
ip = {'a':VS(2,1),'b':1}
c1 = Container(**deepcopy(ip))
c2 = Container(**deepcopy(ip))
print(c1)
print(c2)
c1.a.v = 0
c1.b = 0
print(c1)
print(c2)
"""输出"""
容器(a=VS(v=2, s=1), b=1)
容器(a=VS(v=2, s=1), b=1)
容器(a=VS(v=0, s=1), b=0)
容器(a=VS(v=2, s=1), b=1)
在 OP 的原始示例中,当定义 Container
class 时,将创建一个 VS
对象。然后该对象在 Container
class 的所有实例之间共享。这是一个问题,因为 user-defined class 等 VS
会导致可变对象。因此,更改任何 Container
对象中的 a
将更改所有其他 Container
对象中的 a
你想在每次 Container
class 在初始化时实例化时生成一个新的 VS 对象。为此,使用 field
函数的 default_factory
是一个很好的方法。传递 lambda 函数允许所有这些内联完成。
我在容器中添加了一个 c
成员变量和另一个 VS
class 来说明这样做时成员是独立的。
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)
输出:
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))