#python #json #http #struct #python-dataclasses
Вопрос:
Я искал, но не нашел хорошего ответа, поэтому я опубликую его 🙂
В настоящее время я создаю модуль python, который использует http-запрос get для извлечения объекта с кучей данных, которые структурированы следующим образом.
- Основная группа
- Группа 1
- данные 1
- данные 2
- Группа 2
- данные 1
- данные 2
- Группа 3
- данные 1
- данные 2
- Группа 1
Я создал класс данных, в котором просто перечислены все эти переменные, такие как
@dataclass
class MyData:
grp1_data1: str
grp1_data2: str
grp2_data1: str
grp2_data2: str
grp3_data1: str
grp3_data2: str
@classmethod
def from_dict(cls, data: dict) -> "MyData":
return cls(
grp1_data1=data["Main group"]["Group 1"]["data1"],
grp1_data2=data["Main group"]["Group 1"]["data2"],
# And so on ...
)
То, что я ищу, — это способ сгруппировать переменные внутри класса данных по аналогии со структурой, чтобы мне не нужно было смешивать имя группы и имя данных в имени переменной.
Я довольно новичок в python и не знаю, что это за групповые функции, которые работают с классами данных, если таковые имеются?
Я хотел бы иметь возможность написать что-то подобное grp1.data1=data["Main group"]["Group 1"]["data1"]
или подобное.
Комментарии:
1. Создайте два класса данных, один из которых представляет одну группу, а другой содержит несколько экземпляров этого класса данных группы…?
Ответ №1:
Можно создавать многоуровневые классы данных, чтобы делать то, что вы хотите (возможно, не так элегантно, как структуры типа C, но это работает), используя композицию классов:
@dataclass
class Top:
@dataclass
class Child:
data1: str
data2: str
Group1: Child
Group2: Child
Group3: Child
inst = Top(
Group1=Top.Child('a','b'),
Group2=Top.Child('x', 'y'),
Group3=Top.Child('101', '102')
)
# check it:
@dataclass
class Top:
@dataclass
class Child:
data1: str
data2: str
Group1: Child
Group2: Child
Group3: Child
# create an instance
inst = Top(
Group1=Top.Child('a','b'),
Group2=Top.Child('x', 'y'),
Group3=Top.Child('101', '102')
)
# check it:
assert inst.Group2.data2 == 'y'
Ключ в том, что вы также должны определить все дочерние элементы как классы данных (или, правильнее, как классы).
Вы можете определить дочерние классы на месте(как указано выше) или отдельно.
Комментарии:
1. Идеально, это именно то, что я искал! Спасибо!
Ответ №2:
Ваш вопрос немного неясен, но, как указано в комментариях, было бы лучше иметь единую модель в качестве класса данных, которая представляет данные вашей группы (т. Е. Модель, содержащую data1
data2
поля и), и определить вспомогательную функцию, которая создает сопоставление имени группы экземплярам модели, как показано ниже.
Примечание: Это предполагает, что вы используете Python 3.8 . Для более ранних версий я бы сделал две вещи:
__future__
При необходимости удалите импорт, а вместо этого импортируйтеType
иDict
изtyping
модуля, так как встроенные типы не поддерживают подписанные значения в Python 3.8 или более ранних версиях.- Удалите использование оператора walrus
:=
, который был введен в Python 3.8, и вместо этого используйте следующую за ним строку с комментариями.
# Future import to allow the `int | str` syntax below
# Can be removed for Python 3.10
from __future__ import annotations
from dataclasses import dataclass
from typing import TypeVar
# Create a type that can be `MyData`, or any subclass
D = TypeVar('D', bound='MyData')
@dataclass
class MyData:
data1: str
data2: str
@classmethod
def from_dict(cls: type[D], data: dict, group_num: int | str) -> D:
return cls(
data1=data['MG'][f'G {group_num}']['data1'],
data2=data['MG'][f'G {group_num}']['data2'],
)
@classmethod
def group_to_data(cls: type[D], data: dict) -> dict[int, D]:
return {(group_num := int(group_key.split()[-1])): cls.from_dict(
data, group_num)
for group_key in data['MG']}
# For Python 3.7 or lower, uncomment and use the below instead
# ret_dict = {}
# for group_key in data['MG']:
# group_num = int(group_key.split()[-1])
# ret_dict[group_num] = cls.from_dict(data, group_num)
#
# return ret_dict
Код для тестирования:
def main():
from pprint import pprint
my_data = {
'MG': {
'G 1': {
'data1': 'hello',
'data2': 'World!',
},
'G 2': {
'data1': '',
'data2': 'Testing',
},
'G 3': {
'data1': 'hello 123',
'data2': 'world 321!'
}
}
}
group_to_data = MyData.group_to_data(my_data)
pprint(group_to_data)
# True
assert group_to_data[1] == MyData('hello', 'World!')
Выход:
{1: MyData(data1='hello', data2='World!'),
2: MyData(data1='', data2='Testing'),
3: MyData(data1='hello 123', data2='world 321!')}