是否可以在 python 数据类中对变量进行分组?

Is it possible to group variables in a python dataclass?

我已经搜索过了,但没有找到好的答案,所以我会做一个 post :)

我目前正在创建一个 python 模块,它使用 http get 请求来获取一个对象,其中包含一堆结构如下的数据。

我创建了一个数据类,它只列出了所有这些变量,例如

@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"] 或类似的东西。

你的问题有点不清楚,但正如评论中所建议的,最好有一个 single 模型作为代表你的组数据的数据类(即一个模型包含 data1data2 字段)并定义一个辅助函数,该函数构造组名称到模型实例的映射,如下所示。

注意: 这假设您使用的是 Python 3.8+。对于早期版本,我会做两件事:

  • 如果需要,删除 __future__ 导入,而是从 typing 模块导入 TypeDict,因为内置类型不支持中的下标值Python 3.8 或更早版本。
  • 删除 Python 3.8 中引入的 walrus := 运算符的用法,而是使用其后的注释行。
# 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!')}

可以使用 class 组合创建多级数据classes 来做你想做的事情(可能不像 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'

关键是您必须将所有子成员也定义为数据classes(或者更准确地说是classes)。 您可以就地(如上)或单独定义子 class(es)。