Creating nested dataclass objects in Python(在 Python 中创建嵌套的数据类对象)
问题描述
我有一个数据类对象,其中嵌套了数据类对象.但是,当我创建主对象时,嵌套对象变成了字典:
I have a dataclass object that has nested dataclass objects in it. However, when I create the main object, the nested objects turn into a dictionary:
@dataclass
class One:
f_one: int
@dataclass
class One:
f_one: int
f_two: str
@dataclass
class Two:
f_three: str
f_four: One
data = {'f_three': 'three', 'f_four': {'f_one': 1, 'f_two': 'two'}}
two = Two(**data)
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_2 = Two(**data)
two_2
Two(f_three='three', f_four={'f_one': 1, 'f_two': 'two'})
如您所见,我尝试将所有数据作为字典传递,但没有得到预期的结果.然后我尝试先构造嵌套对象并通过对象构造函数传递它,但我得到了相同的结果.
As you can see I tried to pass all the data as a dictionary, but I didn't get the intended result. Then I tried to construct the nested object first and pass it through the object constructor, but I got the same result.
理想情况下,我想构建我的对象以获得这样的东西:
Ideally I'd like to construct my object to get something like this:
Two(f_three='three', f_four=One(f_one=1, f_two='two'))
除了在访问对象属性时手动将嵌套字典转换为相应的数据类对象之外,还有其他方法可以实现吗?
Is there any way to achieve that other than manually converting nested dictionaries to corresponding dataclass object, whenever accessing object attributes?
提前致谢.
推荐答案
这是一个与 dataclasses
模块本身一样复杂的请求,这意味着这可能是实现这一目标的最佳方式.嵌套字段"能力是定义一个新的装饰器,类似于 @dataclass
.
This is a request that is as complex as the dataclasses
module itself, which means that probably the best way to achieve this "nested fields" capability is to define a new decorator, akin to @dataclass
.
幸运的是,如果您不需要 __init__
方法的签名来反映字段及其默认值,例如通过调用 dataclass
呈现的类,这可以是简单得多:一个将调用原始 dataclass
并在其生成的 __init__
方法上包装一些功能的类装饰器可以使用简单的...(*args, **kwargs):
"样式函数.
Fortunately, if you don't need the signature of the __init__
method to reflect the fields and their defaults, like the classes rendered by calling dataclass
, this can be a whole lot simpler: A class decorator that will call the original dataclass
and wrap some functionality over its generated __init__
method can do it with a plain "...(*args, **kwargs):
" style function.
换句话说,所有需要做的就是围绕生成的 __init__
方法编写一个包装器,该方法将检查kwargs"中传递的参数,检查是否有任何对应于数据类"字段类型",如果是,则在调用原始 __init__
之前生成嵌套对象.也许这用英语比 Python 更难拼写:
In other words, all one needs to do is write a wrapper around the generated __init__
method that will inspect the parameters passed in "kwargs", check if any corresponds to a "dataclass field type", and if so, generate the nested object prior to calling the original __init__
. Maybe this is harder to spell out in English than in 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
注意,除了不用担心 __init__
签名之外,这个也忽略传递 init=False
- 因为它无论如何都是没有意义的.
Note that besides not worrying about __init__
signature, this
also ignores passing init=False
- since it would be meaningless anyway.
(返回行中的 if
负责使其工作,要么使用命名参数调用,要么直接作为装饰器,如 dataclass
本身)
(The if
in the return line is responsible for this to work either being called with named parameters or directly as a decorator, like dataclass
itself)
在交互式提示上:
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
模块本身中的私有辅助函数来创建一个新的 __init__
.
If you want the signature to be kept, I'd recommend using the private helper functions in the dataclasses
module itself, to create a new __init__
.
这篇关于在 Python 中创建嵌套的数据类对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:在 Python 中创建嵌套的数据类对象
基础教程推荐
- 何时使用 os.name、sys.platform 或 platform.system? 2022-01-01
- Dask.array.套用_沿_轴:由于额外的元素([1]),使用dask.array的每一行作为另一个函数的输入失败 2022-01-01
- 用于分类数据的跳跃记号标签 2022-01-01
- 筛选NumPy数组 2022-01-01
- 如何让 python 脚本监听来自另一个脚本的输入 2022-01-01
- 如何在海运重新绘制中自定义标题和y标签 2022-01-01
- 线程时出现 msgbox 错误,GUI 块 2022-01-01
- Python kivy 入口点 inflateRest2 无法定位 libpng16-16.dll 2022-01-01
- 使用PyInstaller后在Windows中打开可执行文件时出错 2022-01-01
- 在 Python 中,如果我在一个“with"中返回.块,文件还会关闭吗? 2022-01-01