如何制作可使用 dataclasses.asdict() 进行序列化的自定义类?

问题描述 投票:0回答:2

我试图在我的应用程序中使用数据类作为(更强类型)字典,并在数据类中使用自定义类型时发现这种奇怪的行为。我在 Windows 上使用 Python 3.11.3。

from dataclasses import dataclass, asdict

class CustomFloatList(list):
    def __init__(self, args):
        for i, arg in enumerate(args):
            assert isinstance(arg, float), f"Expected index {i} to be a float, but it's a {type(arg).__name__}"

        super().__init__(args)

    @classmethod
    def from_list(cls, l: list[float]):
        return cls(l)

@dataclass
class Poc:
    x: CustomFloatList

p = Poc(x=CustomFloatList.from_list([3.0]))
print(p)  # Prints Poc(x=[3.0])
print(p.x)  # Prints [3.0]
print(asdict(p))  # Prints {'x': []}

如果我使用常规列表[float],则不会发生这种情况,但我在这里使用自定义类来强制执行一些运行时约束。

如何正确执行此操作?

我愿意直接使用

.__dict__
,但我认为
asdict()
是处理这个问题的更“官方”的方式

简单的修改使代码的行为符合预期,但效率稍低:

from dataclasses import dataclass, asdict

class CustomFloatList(list):
    def __init__(self, args):
        dup_args = list(args)
        for i, arg in enumerate(dup_args):
            assert isinstance(arg, float), f"Expected index {i} to be a float, but it's a {type(arg).__name__}"

        super().__init__(dup_args)

    @classmethod
    def from_list(cls, l: list[float]):
        return cls(l)

@dataclass
class Poc:
    x: CustomFloatList

p = Poc(x=CustomFloatList.from_list([3.0]))
print(p)
print(p.x)
print(asdict(p))
python python-3.x generator python-dataclasses
2个回答
1
投票

如果您查看 asdict

 源代码,您会发现它传递了一个生成器表达式,当遇到列表时,该表达式会在列表的元素上递归地调用自身:

    elif isinstance(obj, (list, tuple)):
        # Assume we can create an object of this type by passing in a
        # generator (which is not true for namedtuples, handled
        # above).
        return type(obj)(_asdict_inner(v, dict_factory) for v in obj)

但是 您的实现会在

__init__
调用
之前耗尽它进入 
super
的所有迭代器。

不要这样做。如果您想使用超类构造函数,则必须“缓存”这些值。比如:

class CustomFloatList(list):
    def __init__(self, args):
        args = list(args)
        for i, arg in enumerate(args):
            assert isinstance(arg, float), f"Expected index {i} to be a float, but it's a {type(arg).__name__}"

        super().__init__(args)

或者也许:

class CustomFloatList(list):
    def __init__(self, args):
        for i, arg in enumerate(args):
            assert isinstance(arg, float), f"Expected index {i} to be a float, but it's a {type(arg).__name__}"
            self.append(arg)

0
投票

这并非特定于

asdict()
。问题是您的子类与真正的
list
类不兼容,因为当输入是生成器时,
__init__()
方法无法正常工作。您可以更简单地看到问题

print(CustomFloatList(float(x) for x in range(10)))

这将打印一个空列表。

您在循环中使用生成器来检查所有元素是否为浮点数,然后在调用

super().__init__()
时传递空生成器。

更改它,以便一次提取内容。

class CustomFloatList(list):
    def __init__(self, args):
        temp = list(args)
        for i, arg in enumerate(temp):
            assert isinstance(arg, float), f"Expected index {i} to be a float, but it's a {type(arg).__name__}"

        super().__init__(temp)

    @classmethod
    def from_list(cls, l: list[float]):
        return cls(l)
© www.soinside.com 2019 - 2024. All rights reserved.