【问题标题】:Nested dataclass initialization嵌套数据类初始化
【发布时间】:2021-10-16 10:08:53
【问题描述】:

我有一个 JSON 对象,内容如下:

j = {"id": 1, "label": "x"}

我有两种类型:

class BaseModel:
    def __init__(self, uuid):
        self.uuid = uuid

class Entity(BaseModel):
    def __init__(self, id, label):
        super().__init__(id)
        self.name = name

注意id 如何在BaseModel 中存储为uuid

我可以从 JSON 对象加载Entity

entity = Entity(**j)

我想利用 dataclass 重写我的模型:

@dataclass
class BaseModel:
    uuid = str

@dataclass
class Entity:
    name = str

由于我的 JSON 对象没有uuid,基于​​dataclass 的模型上的entity = Entitye(**j) 将抛出以下错误:

TypeError: __init__() 得到了一个意外的关键字参数 'id'

我能想到的“丑陋”的解决方案:

  • 在初始化之前在 JSON 中将 id 重命名为 uuid

    j["uuid"] = j.pop("id")
    
  • 同时定义iduuid

    @dataclass 
    class BaseModel:
        uuid = str
    
    @dataclass
    class Entity:
        id = str
        name = str
    
        # either use:
        uuid = id
        # or use this method
        def __post_init__(self):
            super().uuid = id
    

对于dataclass领域中的这种对象初始化,有没有更简洁的解决方案?

【问题讨论】:

    标签: python json inheritance python-dataclasses


    【解决方案1】:

    可能会破坏删除原始__init__ 的想法,但是编写一个函数来初始化数据类怎么样?

    def init_entity(j):
        j["uuid"] = j.pop("id")
        return Entity(**j)
    

    在你的代码中entity = initEntity(j)

    【讨论】:

    • 感谢您的建议,但我想这违背了我的目的。
    • dataclass是如何实现的?如果您想为两者保留相同的类装饰器,那么您应该通过接受参数重新实现它并在内部创建一个案例研究isinstance 然后是参数键,或者在内部使用hasattribute 然后添加案例研究
    【解决方案2】:

    我认为这里的答案可能是定义一个classmethod 作为数据类的替代构造函数。

    from dataclasses import dataclass
    from typing import TypeVar, Any
    
    @dataclass
    class BaseModel:
        uuid: str
    
    
    E = TypeVar('E', bound='Entity')
    
    
    @dataclass
    class Entity(BaseModel):
        name: str
    
        @classmethod
        def from_json(cls: type[E], **kwargs: Any) -> E:
            return cls(kwargs['id'], kwargs['label']
    

    (对于from_json 类型注释,如果您使用的是python typing.Type[E] 而不是type[E]。)

    请注意,您需要在数据类的主体中为类型注释使用冒号,而不是像您之前所做的那样使用 = 运算符。

    交互式 REPL 中的使用示例:

    >>> my_json_dict = {'id': 1, 'label': 'x'}
    >>> Entity.from_json(**my_json_dict)
    Entity(uuid=1, name='x')
    

    然而,这节省了多少样板代码仍然是个问题。如果您发现自己做了这么多工作来复制非数据类类的行为,通常最好只使用非数据类类。数据类并不是所有问题的完美解决方案,它们也不是。

    【讨论】:

    • 感谢您的建议,虽然这会起作用,但不会在样板文件上节省太多。
    【解决方案3】:

    最简单的解决方案似乎是使用支持键重映射的高效 JSON 序列化库。实际上有 tons 支持此功能,但 dataclass-wizard 是支持此特定用例的(较新)库的一个示例。

    这是一种使用 dataclasses.field() 别名的方法,它应该对 IDE 足够友好:

    from dataclasses import dataclass
    
    from dataclass_wizard import json_field, fromdict, asdict
    
    
    @dataclass
    class BaseModel:
        uuid: int = json_field('id', all=True)
    
    
    @dataclass
    class Entity(BaseModel):
        name: str = json_field('label', all=True)
    
    
    j = {"id": 1, "label": "x"}
    
    # De-serialize the dictionary object into an `Entity` instance.
    e = fromdict(Entity, j)
    
    repr(e)
    # Entity(uuid=1, name='x')
    
    # Assert we get the same object when serializing the instance back to a
    # JSON-serializable dict.
    assert asdict(e) == j
    

    【讨论】:

      猜你喜欢
      • 2016-08-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多