递归创建基于嵌套词典的数据类

发布于 2025-01-26 13:38:18 字数 810 浏览 4 评论 0原文

我有一个名为Config的数据级,该数据是通过字典的属性和值创建的。由于该字典可以具有嵌套词典,因此我想将嵌套词典作为配置对象。这是一个示例:

## Dummy example of a config dict
data = {
  'a' : 1,
  'b' : [2,2,2],
  'c': {
    'c_1' : 3.1
  }
}

final_config = create_config(data)

# Expected result
Config(a=1, b=[2,2,2], c=Config(c_1=3.1) )

这是我出现的内容,使用dataClasses.make_dataclass

def _Config(params_dict):
  config = make_dataclass('Config', params_dict.keys())
  return config(**params_dict)
  
def get_inner_dict(d):
    for _, v in d.items():
        if isinstance(v, dict):
            return get_inner_dict(v) 
        else:
            return _Config(**d)

不幸的是,这无效,因为递归将在找到单个值时尝试创建数据胶流对象。我觉得自己的方式正确,但无法弄清楚需要改变什么。

I have a dataclass called Config that is created through the properties and values of a dictionary. Since this dictionary can have nested dictionaries, i would like to make nested dictionaries as Config objects. Here is an example:

## Dummy example of a config dict
data = {
  'a' : 1,
  'b' : [2,2,2],
  'c': {
    'c_1' : 3.1
  }
}

final_config = create_config(data)

# Expected result
Config(a=1, b=[2,2,2], c=Config(c_1=3.1) )

Here is what i've came up, using dataclasses.make_dataclass:

def _Config(params_dict):
  config = make_dataclass('Config', params_dict.keys())
  return config(**params_dict)
  
def get_inner_dict(d):
    for _, v in d.items():
        if isinstance(v, dict):
            return get_inner_dict(v) 
        else:
            return _Config(**d)

Unfortunately, this doesn't work because the recursion will try to create a dataclass object when it finds a single value. I feel like i'm in the right way, but couldn't figure out what needs to change.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

瑶笙 2025-02-02 13:38:18

在这种情况下,看来您(从技术上讲)不需要使用 dataclasses make_dataclass

您可以使用@STEF提到的__ dict __更新方法来实现自定义类。查看以下示例:

from __future__ import annotations


## Dummy example of a config dict
data = {
    'a': 1,
    'b': [2, 2, 2],
    'c': {
        'c_1': 3.1
    },
    'd': [
        1,
        '2',
        {'k1': 'v1'}
    ]
}

_CONTAINER_TYPES = (dict, list)


class Config:

    def __init__(self, **kwargs):
        self.__dict__ = kwargs

    @classmethod
    def create(cls, data: dict | list) -> Config | list:
        if isinstance(data, list):
            return [cls.create(e) if isinstance(e, _CONTAINER_TYPES) else e
                    for e in data]

        new_data = {
            k: cls.create(v) if isinstance(v, _CONTAINER_TYPES) else v
            for k, v in data.items()
        }

        return cls(**new_data)

    def __repr__(self):
        return f"Config({', '.join([f'{name}={val!r}' for name, val in self.__dict__.items()])})"


final_config = Config.create(data)
print(final_config)
# Prints:
#   Config(a=1, b=[2, 2, 2], c=Config(c_1=3.1), d=[1, '2', Config(k1='v1')])

It looks like you (technically) don't need to use dataclasses or make_dataclass in this scenario.

You can implement a custom class with a __dict__ update approach as mentioned by @Stef. Check out the following example:

from __future__ import annotations


## Dummy example of a config dict
data = {
    'a': 1,
    'b': [2, 2, 2],
    'c': {
        'c_1': 3.1
    },
    'd': [
        1,
        '2',
        {'k1': 'v1'}
    ]
}

_CONTAINER_TYPES = (dict, list)


class Config:

    def __init__(self, **kwargs):
        self.__dict__ = kwargs

    @classmethod
    def create(cls, data: dict | list) -> Config | list:
        if isinstance(data, list):
            return [cls.create(e) if isinstance(e, _CONTAINER_TYPES) else e
                    for e in data]

        new_data = {
            k: cls.create(v) if isinstance(v, _CONTAINER_TYPES) else v
            for k, v in data.items()
        }

        return cls(**new_data)

    def __repr__(self):
        return f"Config({', '.join([f'{name}={val!r}' for name, val in self.__dict__.items()])})"


final_config = Config.create(data)
print(final_config)
# Prints:
#   Config(a=1, b=[2, 2, 2], c=Config(c_1=3.1), d=[1, '2', Config(k1='v1')])
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文