【发布时间】:2019-01-18 23:46:04
【问题描述】:
在下面的代码中,astuple 函数正在执行数据类的类属性的深层复制。为什么它不会产生与函数my_tuple 相同的结果?
import copy
import dataclasses
@dataclasses.dataclass
class Demo:
a_number: int
a_bool: bool
classy: 'YOhY'
def my_tuple(self):
return self.a_number, self.a_bool, self.classy
class YOhY:
def __repr__(self):
return (self.__class__.__qualname__ + f" id={id(self)}")
why = YOhY()
print(why) # YOhY id=4369078368
demo = Demo(1, True, why)
print(demo) # Demo(a_number=1, a_bool=True, classy=YOhY id=4369078368)
untrupled = demo.my_tuple()
print(untrupled) # YOhY id=4369078368
trupled = dataclasses.astuple(demo)
print(trupled) # YOhY id=4374460064
trupled2 = trupled
print(trupled2) # YOhY id=4374460064
trupled3 = copy.copy(trupled)
print(trupled3) # YOhY id=4374460064
trupled4 = copy.deepcopy(trupled)
print(trupled4) # YOhY id=4374460176
脚注
正如Anthony Sottile's 出色的响应清楚地表明这是编码到 Python 3.7 中的行为。任何希望 astuple 以与 collections.namedtuple 相同的方式解包的人都需要将其替换为类似于 Demo.my_tuple 的方法。以下代码没有 my_tuple 脆弱,因为如果数据类的字段发生更改,则不需要修改。另一方面,如果 __slots__ 正在使用中,它将不起作用。
只要类或其超类中存在__hash__ 方法,这两个版本的代码都会构成威胁。请参阅unsafe_hash 的 Python 3.7 文档,特别是以“以下是管理 __hash__() 方法的隐式创建的规则”开头的两段。
def unsafe_astuple(self):
return tuple([self.__dict__[field.name] for field in dataclasses.fields(self)])
【问题讨论】:
-
猜测是为了避免别名问题,因此如果某些代码改变了复制对象中的值,则更改不会反映在数据类中的“原始”对象中。
-
想知道这个 API 会提供一个参数,这样我们就不会总是深拷贝。
标签: python python-3.x python-dataclasses