来自嵌套数据类的字典

slq*_*lqq 6 python python-dataclasses

我想知道如何将嵌套数据类转换为 dict 除了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
Run Code Online (Sandbox Code Playgroud)

预期结果是:

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

{"nested": {"contacts": []}}
Run Code Online (Sandbox Code Playgroud)

我目前拥有的:

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

{"nested": {"name": None, "contacts": []}, surname: None}
Run Code Online (Sandbox Code Playgroud)

我知道这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)
Run Code Online (Sandbox Code Playgroud)

但它不像预期的那样工作。

_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)
Run Code Online (Sandbox Code Playgroud)

Ven*_*ath 10

您可以创建一个自定义字典工厂,删除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)
Run Code Online (Sandbox Code Playgroud)

编辑: 基于@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)
Run Code Online (Sandbox Code Playgroud)

  • 将 dict 工厂设置为“dict”子类似乎是一个坏主意。结果将由“CustomDict”实例而不是“dict”实例组成,这会导致与常规字典出现各种奇怪且微妙的不一致。为什么不直接编写一个函数来构建一个没有“None”值的“dict”呢? (2认同)