python - 为什么 dataclasses.astuple 返回类属性的深度拷贝?

标签 python python-3.x python-dataclasses

在下面的代码中,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__ 正在使用中,它将不起作用。

只要类或其父类(super class)中存在 __hash__ 方法,两个版本的代码都会构成威胁。请参阅有关 unsafe_hash 的 Python 3.7 文档,特别是开头的两段“这是管理 __hash__() 方法隐式创建的规则”。

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

最佳答案

这似乎是一个 undocumented astuple 的行为(和 asdict 似乎也是如此)。

dataclasses.astuple(*, tuple_factory=tuple)

Converts the dataclass instance to a tuple (by using the factory function tuple_factory). Each dataclass is converted to a tuple of its field values. dataclasses, dicts, lists, and tuples are recursed into.

这是 the source :

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 似乎是故意的,但可能应该记录在案。

关于python - 为什么 dataclasses.astuple 返回类属性的深度拷贝?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51802109/

相关文章:

python - 在连字符后跟数字之前使用正则表达式提取字符串的一部分

python - 如何强制数据类字段的类型?

python - 在函数中创建不同的对象

python - 解析和处理表格.txt 文件

python - Python中有计算百分比的运算符吗?

python - 我正在寻找一个高效的 django 查询集来阻止这么多人访问我的数据库

python - 当类变量被分配为列表时,Python 中的数据类不会引发错误(但会出现键入提示)

python - 如何组合数据类、属性和 lru_cache

python - 为什么函数运行时不带参数?

python - SQLAlchemy 中的反射不适用于 MS SQL Server 系统表?