首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何创建处理不同名称的嵌套数据集的数据集?

如何创建处理不同名称的嵌套数据集的数据集?
EN

Stack Overflow用户
提问于 2022-03-02 19:04:55
回答 3查看 1.3K关注 0票数 2

我正在使用英雄联盟API中的数据学习Python、JSON和数据类。使用dacite,我创建了允许使用以下语法:champs.data['Ahri']['key']访问数据的父类和子类。但是,我想知道是否有一种方法可以创建一个类,将键作为字段返回,这样就可以使用以下语法访问数据:champs.data.Ahri.key

以下是工作代码:

代码语言:javascript
复制
from dataclasses import dataclass
from dacite import from_dict

j1 = {'type': 'champion',
      'data': {'Aatrox': {'id': 'Aatrox', 'key': '266', 'name': 'Aatrox'},
      'Ahri': {'id': 'Ahri', 'key': '103', 'name': 'Ahri'}}}

@dataclass
class C:
    type: str
    data: dict

@dataclass
class P:
    type: str
    data: dict

champs = from_dict(data_class=P, data=j1)
champs.data['Ahri']['key']
EN

回答 3

Stack Overflow用户

发布于 2022-03-02 22:04:11

您可能能得到的最接近的(至少以一种足够安全的方式)是使用@JonSG建议 (使用champs.data['Ahri'].key )。

下面是一个使用dataclass-wizard的简单示例。它不会像我所知道的dacite那样进行严格的类型检查。

相反,它选择在可能的情况下执行隐式强制割礼,这在某些情况下是有用的;在本例中,您可以看到下面的示例-- str到带注释的int

注意:这个例子应该适用于带有包含的3.7+导入的Python。

代码语言:javascript
复制
from __future__ import annotations

from dataclasses import dataclass
from dataclass_wizard import fromdict


data = {
    'type': 'champion',
    'data': {
          'Aatrox': {'id': 'Aatrox', 'key': '266', 'name': 'Aatrox'},
          'Ahri': {'id': 'Ahri', 'key': '103', 'name': 'Ahri'},
    }
}


@dataclass
class P:
    type: str
    data: dict[str, Character]


@dataclass
class Character:
    id: str
    key: int
    name: str


champs = fromdict(P, data)

print(champs)
print(champs.data['Ahri'].key)

输出:

代码语言:javascript
复制
P(type='champion', data={'Aatrox': Character(id='Aatrox', key=266, name='Aatrox'), 'Ahri': Character(id='Ahri', key=103, name='Ahri')})
103
票数 1
EN

Stack Overflow用户

发布于 2022-03-02 19:52:26

如何做到这一点

代码语言:javascript
复制
d = {
    "type": "champion",
    "data": {
        "Aatrox": {"id": "Aatrox", "key": "266", "name": "Aatrox"},
        "Ahri": {"id": "Ahri", "key": "103", "name": "Ahri"},
    },
}


def dict_to_class(d) -> object:
    if isinstance(d, dict):

        class C:
            pass

        for k, v in d.items():
            setattr(C, k, dict_to_class(v))
        return C
    else:
        return d


champ = dict_to_class(d)

print(champ.data.Ahri.key)
# 103

这里的关键是setatter内置方法,它接受一个对象、一个字符串和一些值,并在该对象上创建一个属性(字段),该属性(字段)根据字符串命名并包含该值。

别这么做!

我必须强调,几乎没有任何好的理由这样做。在处理形状未知的JSON数据时,表示它的正确方法是a dict

如果您确实知道数据的形状,您应该创建一个专门的dataclass,如下所示:

代码语言:javascript
复制
from dataclasses import dataclass

d = {
    "type": "champion",
    "data": {
        "Aatrox": {"id": "Aatrox", "key": "266", "name": "Aatrox"},
        "Ahri": {"id": "Ahri", "key": "103", "name": "Ahri"},
    },
}

@dataclass
class Champion:
    id: str
    key: str
    name: str

champions = {name: Champion(**attributes) for name, attributes in d["data"].items()}

print(champions)
# {'Aatrox': Champion(id='Aatrox', key='266', name='Aatrox'), 'Ahri': Champion(id='Ahri', key='103', name='Ahri')}

print(champions["Aatrox"].key)
# 266
票数 0
EN

Stack Overflow用户

发布于 2022-03-02 20:31:39

dacite文档中有一个关于嵌套结构的部分,非常接近您想要的内容。他们使用的逐字示例如下:

代码语言:javascript
复制
@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))

我们可以访问任意深度的字段,例如result.a.x == 'test'

这与您的数据之间的关键区别在于,data键下的字典具有带有任意值的键(AatroxAhri等)。dacite并不是为了动态创建新的字段名而设置的,所以您将得到的最佳结果类似于@JonSG的回答的后半部分,后者使用setattr动态地构建新字段。

不过,让我们想象一下您将如何使用这些数据。您可能希望某个点能够迭代您的冠军,以便执行筛选/转换/等操作。在python中迭代字段是可能的,但您必须深入研究python内部,这意味着代码的可读性和通俗性较差。

更好的办法是:

  1. j1预处理为适合您想要使用的结构的形状,然后使用dacite和适合新结构的dataclass。例如,可能将data的值提取到列表中是有意义的。
  2. 使用dacite分步骤进行处理。例如,如下所示:
代码语言:javascript
复制
from dataclasses import dataclass
from dacite import from_dict


@dataclass
class TopLevel:
    type: str
    data: dict


j1 = {
    "type": "champion",
    "data": {
        "Aatrox": {"id": "Aatrox", "key": "266", "name": "Aatrox"},
        "Ahri": {"id": "Ahri", "key": "103", "name": "Ahri"},
    },
}

champions = from_dict(data_class=TopLevel, data=j1)
# champions.data is a dict of dicts

@dataclass
class Champion:
    id: str
    key: str
    name: str


# transform champions.data into a dict of Champions
for k, v in champions.data.items():
    champions.data[k] = from_dict(data_class=Champion, data=v)

# now, you can do interesting things like the following filter operation
start_with_a = [
    champ for champ in champions.data.values() if champ.name.lower().startswith("a")
]
print(start_with_a)
# [Champion(id='Aatrox', key='266', name='Aatrox'), Champion(id='Ahri', key='103', name='Ahri')]
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71327925

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档