如何创建一个处理具有不同名称的嵌套字典的数据类?
我正在使用英雄联盟 API 中的数据来学习 Python、JSON 和数据类。我使用英安岩创建了父类和子类,允许使用以下语法访问数据:champs.data['Ahri']['key']。但是,我想知道是否有一种方法可以创建一个将键作为字段返回的类,以便可以使用以下语法访问数据:champs.data.Ahri.key
。
这是工作代码:
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']
I am using the data from the League of Legends API to learn Python, JSON, and Data Classes. Using dacite
, I have created parent and child classes that allow access to the data using this syntax: champs.data['Ahri']['key']
. However, I wonder if there is a way to create a class that returns the keys as fields so one could access the data using this syntax: champs.data.Ahri.key
.
Here is the working code:
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']
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
如果是我,我可能会留下/制作
champions
一本字典。 访问它然后像
champions['Ahri'].key
一样:结果
103
但是,如果您真的热衷于
champions.Ahri.key
> 然后你可以将 Champions 实现为一个空类,并再次使用
setattr()
给出103
注意:
@dataclass
装饰器可能会被省略来自冠军()。If it were me, I would probably leave/make
champions
a dictionary. Then access it likechampions['Ahri'].key
Something like:
resulting in
103
However if you were really keen on
champions.Ahri.key
then you can implement Champions as an empty class and usesetattr()
again giving you
103
Note: The
@dataclass
decorator can likely be omitted from Champion().您可能获得的最接近的结果 - 至少以足够安全的方式 - 正如@JonSG建议的,使用
champs.data['Ahri'].key
。这是一个使用
dataclass-wizard
的简单示例。它不像我所知的英安岩那样进行严格类型检查。相反,它选择在可能的情况下进行隐式类型的强制转换,这在某些情况下很有用;您可以在下面看到一个示例 - 在本例中,将
str
转换为带注释的int
。输出:
The closest you can probably get - at least in a safe enough manner - is as @JonSG suggests, using
champs.data['Ahri'].key
.Here's a straightforward example using the
dataclass-wizard
. It doesn't do a strict type checking as I knowdacite
does.Instead, it opts to do implicit type coercision where possible, which is useful in some cases; you can see an example of this below -
str
to annotatedint
in this case.Output:
如何做到这一点
这里的关键是
setatter
内置方法,它接受一个对象、一个字符串和一些值,并在该对象上创建一个属性(字段),根据字符串命名并包含值。不要这样做!
我必须强调,这样做几乎没有充分的理由。当处理未知形状的 JSON 数据时,表示它的正确方法是一个
dict
。如果您确实知道数据的形状,则应该创建一个专门的数据类,如下所示:
How to do this
The key here is the
setatter
builtin method, which takes an object, a string, and some value, and creates an attribute (field) on that object, named according to the string and containing the value.Don't do this!
I must stress that there is almost never a good reason to do this. When dealing with JSON data of an unknown shape, the correct way to represent it is a
dict
.If you do know the shape of the data, you should create a specialized
dataclass
, like so:英安岩文档有一个关于 嵌套结构 的部分,非常接近您想要的内容。他们逐字使用的示例如下:
我们可以访问任意深度的字段,例如
result.ax == 'test'
。此数据与您的数据之间的关键区别在于
data
键下的字典具有具有任意值的键(Aatrox
、Ahri
等) 。英安岩未设置为动态创建新的字段名称,因此您将获得的最好结果类似于@JonSG 的后半部分 answer,使用setattr
动态构建新字段。不过,让我们想象一下您将如何使用这些数据。也许您希望某个点能够迭代您的冠军,以便执行过滤/转换等。手术。可以迭代python中的字段,但你必须真正深入研究python内部,这意味着你的代码会更少可读/一般可理解。
更好的方法是采用以下方法之一:
j1
预处理为适合您要使用的结构的形状,然后使用英安岩和适合新结构的dataclass
。例如,将data
字典的值拉出到列表中也许是有意义的。The dacite docs have a section about nested structures that is very close to what you want. The example they use, verbatim, is as follows:
We can access fields at arbitrary depth as e.g.
result.a.x == 'test'
.The critical difference between this and your data is that the dictionary under the
data
key has keys with arbitrary values (Aatrox
,Ahri
, etc.). dacite isn't set up to create new field names on the fly, so the best you're going to get is something like the latter part of @JonSG's answer, which usessetattr
to dynamically build new fields.Let's imagine how you would use this data for a moment, though. Probably you'd want a some point to be able to iterate over your champions in order to perform a filter/transform/etc. operation. It's possible to iterate over fields in python, but you have to really dig into python internals, which means your code will be less readable/generally comprehensible.
Much better would be one of the following:
j1
into a shape that fits the structure you want to use, and then use dacite with adataclass
that fits the new structure. For example, maybe it makes sense to pull the values of thedata
dict out into a list.