6

在下面的代码中,该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 的出色回应所表明的那样,这是 Python 3.7 中编码的行为。任何希望 astuple 以与 collections.namedtuple 相同的方式解压的人都需要用类似于 .namedtuple 的方法替换它Demo.my_tuple。以下代码没有 my_tuple 脆弱,因为如果数据类的字段发生更改,则不需要修改。另一方面,如果__slots__在使用中,它将不起作用。

__hash__每当类或其超类中存在方法时,这两个版本的代码都会构成威胁。请参阅 Python 3.7 文档unsafe_hash,特别是“以下是管理隐式创建方法的规则”开头的两段__hash__()

def unsafe_astuple(self):
    return tuple([self.__dict__[field.name] for field in dataclasses.fields(self)])
4

1 回答 1

4

这似乎是(而且似乎也是)的无证行为。astupleasdict

dataclasses.astuple(*, tuple_factory=tuple)

将数据类转换instance为元组(通过使用工厂函数tuple_factory)。每个数据类都转换为其字段值的元组。数据类、字典、列表和元组被递归到。

这是来源

def _asdict_inner(obj, dict_factory):
    if _is_dataclass_instance(obj):
        result = []
        for f in fields(obj):
            value = _asdict_inner(getattr(obj, f.name), dict_factory)
            result.append((f.name, value))
        return dict_factory(result)
    elif isinstance(obj, (list, tuple)):
        return type(obj)(_asdict_inner(v, dict_factory) for v in obj)
    elif isinstance(obj, dict):
        return type(obj)((_asdict_inner(k, dict_factory), _asdict_inner(v, dict_factory))
                          for k, v in obj.items())
    else:
return copy.deepcopy(obj)

这里的 deepcopy 似乎是故意的,尽管可能应该记录在案。

于 2018-08-11T18:37:24.903 回答