当前位置: 首页 > 知识库问答 >
问题:

在Python中创建嵌套数据类对象

齐铭
2023-03-14

我有一个数据类对象,其中包含嵌套的数据类对象。但是,当我创建主对象时,嵌套的对象会变成字典:

@dataclass
class One:
    f_one: int
    f_two: str
    
@dataclass
class Two:
    f_three: str
    f_four: One


Two(**{'f_three': 'three', 'f_four': {'f_one': 1, 'f_two': 'two'}})

Two(f_three='three', f_four={'f_one': 1, 'f_two': 'two'})

obj = {'f_three': 'three', 'f_four': One(**{'f_one': 1, 'f_two': 'two'})}

Two(**obj)
Two(f_three='three', f_four=One(f_one=1, f_two='two'))

如您所见,只有**obj有效。

理想情况下,我想构造我的对象来获得这样的东西:

Two(f_three='three', f_four=One(f_one=1, f_two='two'))

除了在访问对象属性时手动将嵌套字典转换为相应的数据类对象之外,还有别的方法可以实现这一点吗?

共有3个答案

詹正浩
2023-03-14

您可以尝试dacite模块。这个包简化了从字典创建数据类-它还支持嵌套结构。

例子:

from dataclasses import dataclass
from dacite import from_dict

@dataclass
class A:
    x: str
    y: int

@dataclass
class B:
    a: A

data = {
    'a': {
        'x': 'test',
        'y': 1,
    }
}

result = from_dict(data_class=B, data=data)

assert result == B(a=A(x='test', y=1))

要安装英安岩,只需使用pip:

$ pip install dacite
曾华翰
2023-03-14

为此,您可以使用post_init

from dataclasses import dataclass
@dataclass
class One:
    f_one: int
    f_two: str

@dataclass
class Two:
    f_three: str
    f_four: One
    def __post_init__(self):
        self.f_four = One(**self.f_four)

data = {'f_three': 'three', 'f_four': {'f_one': 1, 'f_two': 'two'}}

print(Two(**data))
# Two(f_three='three', f_four=One(f_one=1, f_two='two'))
莘康裕
2023-03-14

这是一个与dataclasses模块本身一样复杂的请求,这意味着实现这种“嵌套字段”功能的最佳方法可能是定义一个新的修饰符,类似于@dataclass

幸运的是,如果您不需要__init__方法的签名来反映字段及其默认值,例如通过调用dataclass呈现的类,这可以简单得多:一个类装饰器将调用原始dataclass并在其生成的__init__方法上包装一些功能,可以用一个简单的"…(*args,**kwargs):"样式函数来完成。

换句话说,所需要做的就是围绕生成的__init__方法编写一个包装器,该包装器将检查以“kwargs”传递的参数,检查是否有任何参数对应于“数据类字段类型”,如果有,则在调用原始__init__之前生成嵌套对象。也许这在英语中比在Python中更难拼写:

from dataclasses import dataclass, is_dataclass

def nested_dataclass(*args, **kwargs):
    def wrapper(cls):
        cls = dataclass(cls, **kwargs)
        original_init = cls.__init__
        def __init__(self, *args, **kwargs):
            for name, value in kwargs.items():
                field_type = cls.__annotations__.get(name, None)
                if is_dataclass(field_type) and isinstance(value, dict):
                     new_obj = field_type(**value)
                     kwargs[name] = new_obj
            original_init(self, *args, **kwargs)
        cls.__init__ = __init__
        return cls
    return wrapper(args[0]) if args else wrapper

注意,除了不用担心< code>__init__签名之外,这还忽略了传递< code>init=False -因为这无论如何都是没有意义的。

(返回行中的 if 负责使用命名参数调用或直接作为装饰器(如数据类本身)工作)

并在互动提示上:

In [85]: @dataclass
    ...: class A:
    ...:     b: int = 0
    ...:     c: str = ""
    ...:         

In [86]: @dataclass
    ...: class A:
    ...:     one: int = 0
    ...:     two: str = ""
    ...:     
    ...:         

In [87]: @nested_dataclass
    ...: class B:
    ...:     three: A
    ...:     four: str
    ...:     

In [88]: @nested_dataclass
    ...: class C:
    ...:     five: B
    ...:     six: str
    ...:     
    ...:     

In [89]: obj = C(five={"three":{"one": 23, "two":"narf"}, "four": "zort"}, six="fnord")

In [90]: obj.five.three.two
Out[90]: 'narf'

如果希望保留签名,我建议使用dataclasses模块本身中的私有helper函数来创建一个新的__init__

 类似资料:
  • 问题内容: 我有一个其中嵌套了数据类对象的数据类对象。但是,当我创建主对象时,嵌套的对象会变成字典: 如您所见,我试图将所有数据作为字典传递,但没有得到预期的结果。然后,我尝试先构造嵌套对象,然后将其传递给对象构造函数,但得到的结果相同。 理想情况下,我想构造我的对象以获得以下内容: 除了在访问对象属性时手动将嵌套字典转换为相应的数据类对象之外,还有什么方法可以实现? 提前致谢。 问题答案: 这是

  • 问题内容: 我不太使用php,并且在对象创建方面有些模糊。我需要发出一个发送json的网络服务请求,我想我已经覆盖了那部分。在提交数据之前,我需要创建一个嵌套对象。根据我对基于ecma的脚本语言的经验,我认为这是微不足道的,但是我发现该语法难以导航。我要创建的对象如下。 我已经看到了许多平面对象的示例,但是还没有找到嵌套对象的最小示例。上面对象的php语法是什么?这是在PHP中做的不寻常的事情吗?

  • 问题内容: 我正在尝试从JSON创建嵌套的UL。我能够遍历并从对象中获取数据,但是在构建嵌套UL时遇到了麻烦。我认为’.append’方法放置在错误的位置。生成的LI都分组在一起。我如何创建一个循环(或者也可以用另一种方法)来构建带有正确嵌套的子菜单LI的UL?我曾尝试使用其他类似的帖子来解决我的问题,但是我的数据和代码似乎没有任何意义。对此有些不解之举- 我尝试了几种方法来创建此动态列表,但到目

  • 问题内容: 我有2个CSV文件:“数据”和“映射”: ‘映射’文件有4列:,,,和。填充所有四个列。 “数据”文件具有这些相同的列,其中填充了列,而其他三列为空白。 我希望我的Python代码来打开这两个文件并为每个数据文件,它的映射,以及从映射文件中值。 我知道只有2列存在时才需要使用dict(需要映射1列),但是当需要映射3列时我不知道如何实现。 以下是我尝试完成的映射的代码: 它返回。 经过

  • 问题内容: 如何将JSON传递到RAILS应用程序,以便它将以has_many关系创建嵌套的子对象? 这是我到目前为止的内容: 两个模型对象。 使用通勤,我可以设置标准控制器。我希望能够使用JSON在一个REST调用中创建一个Commute对象以及几个子Location对象。我一直在尝试这样的事情: 更具可读性的JSON是: 执行该命令时,将得到以下输出: 看起来好像正在读取JSON数组的loca

  • 问题内容: 我设法定义一个类A,使用另一个类B的实例列表作为类A的实例变量。B类具有更改A类的另一个实例变量a1的功能。类A还具有更改类B的实例变量bb的功能。因此,A类可以访问B类,而B类可以访问A类。这两个类嵌套在一起。我知道我们可以简化将B类的所有实例变量和函数更改为A类的事情。但是在我的项目中,这种嵌套结构才是真正的东西。 我想知道的是,这种嵌套类是否会降低python的效率?有更好的解决