【问题标题】:Dict from nested dataclasses来自嵌套数据类的字典
【发布时间】:2020-04-16 07:35:52
【问题描述】:

我想知道除了None 字段之外,如何将嵌套数据类转换为字典。我知道asdict() 方法的存在,但我正在尝试编写类似于asdict() 的方法,该方法将在dict 创建期间忽略空值。

例如:

@dataclass
class Nested:
    name: str = None
    contacts: list = field(default_factory = list)

@dataclass
class Main:
    nested: Nested = field(default_factory = Nested)
    surname: str = None

预期结果是:

example = Main()
example_d = asdict(example)
print(example_d)

{"nested": {"contacts": []}}

我目前拥有的:

example = Main()
example_d = asdict(example)
print(example_d)

{"nested": {"name": None, "contacts": []}, surname: None}

我知道asdict() 会忽略没有值的字段并将默认值作为默认值,但在数据类初始化期间我需要= None

我最终覆盖了 asdict()_asdict_inner() 方法:

def asdict(obj, *, dict_factory=dict):
    if not _is_dataclass_instance(obj):
        raise TypeError("asdict() should be called on dataclass instances")
    return _asdict_inner(obj, dict_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, tuple) and hasattr(obj, '_fields'):
        return type(obj)(*[_asdict_inner(v, dict_factory) for v in obj])
    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() if v is not None) # <- Mine change to exclude None values and keys.
    else:
        return copy.deepcopy(obj)

但它并没有按预期工作。

根据要求实施_is_dataclass_instance。它来自dataclass 模块。

def _is_dataclass_instance(obj):
    """Returns True if obj is an instance of a dataclass."""
    return not isinstance(obj, type) and hasattr(obj, _FIELDS)

【问题讨论】:

    标签: python python-dataclasses


    【解决方案1】:

    您可以创建一个自定义字典工厂,删除 None 值键并将其与 asdict() 一起使用。

    class CustomDict(dict):
        def __init__(self, data):
            super().__init__(x for x in data if x[1] is not None)
    
    example = Main()
    example_d = asdict(example, dict_factory=CustomDict)
    

    编辑:根据@user2357112-supports-monica 的建议,这里有一个不使用自定义词典的替代方案。

    def factory(data):
        return dict(x for x in data if x[1] is not None)
    
    
    example = Main()
    example_d = asdict(example, dict_factory=factory)
    

    【讨论】:

    • 是的,就是这样。 :) 队友的欢呼声。我一直在寻找字典工厂的例子,因为我不知道它应该是什么样子。谢谢。
    • 很高兴为您提供帮助 :) 顺便说一句,您可以避免使用 tmp1 = (x for x in data if x[1] is not None) 创建中间列表。
    • 将 dict 工厂设为 dict 子类似乎是个坏主意。结果将由CustomDict 的实例而不是dict 的实例组成,这会导致与常规dicts 的各种奇怪和微妙的不一致。为什么不直接编写一个函数来构建没有 None 值的 dict
    • @user2357112supportsMonica 是的,这是另一种选择。我已经用你的建议更新了答案。虽然我听说过反对自定义词典的论点,但我不确定它们是否适用于上述简单案例。
    猜你喜欢
    • 2019-04-21
    • 2016-02-10
    • 2022-07-14
    • 2016-11-13
    • 2023-03-15
    • 2017-06-16
    • 1970-01-01
    • 2015-05-30
    • 2014-09-20
    相关资源
    最近更新 更多