首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么跨实例共享dataclass字段?

为什么跨实例共享dataclass字段?
EN

Stack Overflow用户
提问于 2022-09-04 11:22:46
回答 1查看 162关注 0票数 2

第一次使用dataclass,也不太擅长Python。以下行为与我迄今的理解相抵触:

代码语言:javascript
复制
from dataclasses import dataclass

@dataclass
class X:
  x: int = 1
  y: int = 2

@dataclass
class Y:
  c1: X = X(3, 4)
  c2: X = X(5, 6)

n1 = Y()
n2 = Y()

print(id(n1.c1))
print(id(n2.c1))

n1.c1.x = 99999
print(n2)

这个指纹

代码语言:javascript
复制
140459664164272
140459664164272
Y(c1=X(x=99999, y=4), c2=X(x=5, y=6))

为什么c1的行为像类变量?我能做些什么来保持n2.c1 != n1.c1,我需要写一个init函数吗?

我可以在Y的基础上得到合理的结果:

代码语言:javascript
复制
  def __init__(self):
   self.c1 = X(3, 4)
   self.c2 = X(5, 6)

指纹:

代码语言:javascript
复制
140173334359840
140173335445072
Y(c1=X(x=3, y=4), c2=X(x=5, y=6))
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-09-04 12:46:35

为什么c1的行为像类变量?

因为您为它们指定了默认值,因此它们现在是一个类属性。在可变默认值部分中,提到了:

Python将默认成员变量值存储在类属性中。

但是看看这个:

代码语言:javascript
复制
@dataclass
class X:
    x: int = 1
    y: int = 2

@dataclass
class Y:
    c1: X
    c2: X = X(5, 6)

print("c1" in Y.__dict__)  # False
print("c2" in Y.__dict__)  # True

c1没有默认值,所以它不在类的名称空间中。

实际上,通过这样做(定义默认值),Python将c1c2存储在实例的名称空间(n1.__dict__)和类的命名空间(Y.__dict__)中。它们是相同的对象,只传递引用:

代码语言:javascript
复制
@dataclass
class X:
    x: int = 1
    y: int = 2

@dataclass
class Y:
    c1: X = X(3, 4)
    c2: X = X(5, 6)

n1 = Y()
n2 = Y()

print("c1" in Y.__dict__)  # True
print("c1" in n1.__dict__)  # True

print(id(n1.c1)) # 140037361903232
print(id(n2.c1)) # 140037361903232
print(id(Y.c1))  # 140037361903232

所以现在,如果你想让它们与众不同,你有几种选择:

  1. 在实例化时传递参数(不是很好的):
代码语言:javascript
复制
@dataclass
class X:
    x: int = 1
    y: int = 2

@dataclass
class Y:
    c1: X = X(3, 4)
    c2: X = X(5, 6)

n1 = Y(X(3, 4), X(5, 6))
n2 = Y(X(3, 4), X(5, 6))

print("c1" in Y.__dict__)  # True
print("c1" in n1.__dict__)  # True

print(id(n1.c1)) # 140058585069264
print(id(n2.c1)) # 140058584543104
print(id(Y.c1))  # 140058585065088
  1. 使用field和pass default_factory
代码语言:javascript
复制
from dataclasses import dataclass, field

@dataclass
class X:
    x: int = 1
    y: int = 2

@dataclass
class Y:
    c1: X = field(default_factory=lambda: X(3, 4))
    c2: X = field(default_factory=lambda: X(5, 6))

n1 = Y()
n2 = Y()

print("c1" in Y.__dict__)   # False
print("c1" in n1.__dict__)  # True

print(id(n1.c1))  # 140284815353136
print(id(n2.c1))  # 140284815353712

在第二个选项中,由于我没有指定default参数(两者都不能混合),所以类的命名空间中不会存储任何内容。field(default=SOMETHING)= SOMETHING的另一种表达方式。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73598938

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档