dataclass 可以继承普通 Python class 的属性吗?

Can a dataclass inherit attributes from a normal Python class?

我有一个遗留物 class,它被用作数据结构。它包含一些属性和一些方法(如过去使用的 from_dict()to_dict())。 class 还从另一个普通基础 class.

继承了一些属性

我希望将所有这些属性移动到一个新的 @dataclass 中。我的新 dataclass 可以从现有的普通 class 继承所有这些旧属性吗?但显然不是方法。

我想实现类似的东西:

from dataclasses import dataclass


class BaseClass1:
    def __init__(
        self,
        id: int,
        type: str,
    ):
        self.id = id
        self.type = type

    def to_dict(self):
        # Dummy code here
        pass

    def from_dict(self):
        # Dummy code here
        pass


class BaseClass2(BaseClass1):
    def __init__(self, speed: float, **kwargs):
        self.speed = speed

        super().__init__(**kwargs)

    def to_dict(self):
        # Dummy code here
        pass

    def from_dict(self):
        # Dummy code here
        pass


@dataclass
class NewDataStructure(BaseClass2):
    color: str
    owner: str


if __name__ == "__main__":
    new_data = NewDataStructure(
        color="red", owner="john", speed=23.7, id=345, type="car"
    )

    print(new_data)

我会在这里使用多重继承,最终 class 从数据 class 和普通基础 class 继承。这样您就可以将初始化转发给基础 __init__ 方法,任何进一步的更改都将自动包含在内。

根据你的例子,我会使用:

@dataclass
class TmpDataStructure():
    color: str
    owner: str

class NewDataStructure(TmpDataStructure, BaseClass2):
    def __init__(self, **kwargs):
        super().__init__(**{k: v for k, v in kwargs.items()
                            if k in TmpDataStructure.__match_args__})
        BaseClass2.__init__(self, **{k: v for k, v in kwargs.items()
                                     if k not in TmpDataStructure.__match_args__})

您将能够安全地做到:

new_data = NewDataStructure(
    color="red", owner="john", speed=23.7, id=345, type="car"
)

print(new_data)

但是你只会得到数据中定义的字段class:

NewDataStructure(color='red', owner='john')

这也将继承 BaseClass2BaseClass1 的方法...

假设所有常规 类 中 __init__ 的函数参数对每个参数都有一个类型注释 - 例如 id: int - 那么下面的方法或修改版本它应该有望在您的情况下起作用,以生成一个近似的数据类模式,给定任意数量的常规 类,sub-class 来自另一个:

from dataclasses import dataclass, fields


class BaseClass1:
    def __init__(
        self,
        id: int,
        type: str,
    ):
        self.id = id
        self.type = type

    def to_dict(self):
        # Dummy code here
        pass

    def from_dict(self):
        # Dummy code here
        pass


class BaseClass2(BaseClass1):
    def __init__(self, speed: float, **kwargs):
        self.speed = speed

        super().__init__(**kwargs)

    def to_dict(self):
        # Dummy code here
        pass

    def from_dict(self):
        # Dummy code here
        pass


@dataclass
class NewDataStructure(BaseClass2):
    color: str
    owner: str


dataclass_fields = []
# start from 1: exclude `NewDataStructure` itself
# exclude last at -1: base for all types is `object`
for cls in reversed(NewDataStructure.__mro__[1:-1]):
    init_fn_annotated_params = getattr(cls.__init__, '__annotations__', {})
    if not init_fn_annotated_params:
        continue
    dataclass_fields.append(f'    # generated from `{cls.__qualname__}`')
    for field, ftype in init_fn_annotated_params.items():
        type_name = getattr(ftype, '__qualname__', ftype.__name__)
        dataclass_fields.append(f'    {field}: {type_name}')

# now finally, print out the generated dataclass schema
print('@dataclass')
print('class NewDataStructure(BaseClass2):')
print('\n'.join(dataclass_fields))
print('    # generated from `NewDataStructure`')
for f in fields(NewDataStructure):
    type_name = getattr(f.type, '__qualname__', f.type.__name__)
    print(f'    {f.name}: {type_name}')

输出:

@dataclass
class NewDataStructure(BaseClass2):
    # generated from `BaseClass1`
    id: int
    type: str
    # generated from `BaseClass2`
    speed: float
    # generated from `NewDataStructure`
    color: str
    owner: str