控制Python数据类继承类时的初始化顺序

Wei*_*Lin 13 python inheritance python-dataclasses

我所知道
的 Python 数据类允许使用数据类或类进行继承。在最佳实践中(以及在其他语言中),当我们进行继承时,应该首先调用初始化。在 Python 中它是:

def __init__(self):
    super().__init__()
    ...
Run Code Online (Sandbox Code Playgroud)

我在做什么
因为数据类是在 Python 3.7 中引入的,所以我正在考虑用数据类替换我的所有类。使用 dataclass,它的好处之一是__init__为您生成。当数据类需要继承基类时,这并不好——例如:

class Base:
    def __init__(self):
        self.a = 1

@dataclass
class Child(Base):
    a:int
    def __post_init__(self):
        super().__init__() 
Run Code Online (Sandbox Code Playgroud)

我的问题
问题是我们必须将超级初始化调用放入__post_init__其中,实际上是dataclass 的init之后调用的。
缺点是我们失去了约定契约,初始化混乱导致我们无法覆盖超类的属性。

可以通过 的概念解决__pre_init__。我已经阅读了该文档,但没有看到与该概念有任何关系。我错过了什么吗?

san*_*ash 8

实际上有一种方法是在之前调用的__init__:它是__new__。所以,你可以做这样的把戏:呼叫Base.__init__Child.__new__。我不能说这是一个好的解决方案,但如果您有兴趣,这里有一个工作示例:

class Base:
    def __init__(self, a=1):
        self.a = a


@dataclass
class Child(Base):
    a: int

    def __new__(cls, *args, **kwargs):
        obj = object.__new__(cls)
        Base.__init__(obj, *args, **kwargs)
        return obj


c = Child(a=3)
print(c.a)  # 3, not 1, because Child.__init__ overrides a
Run Code Online (Sandbox Code Playgroud)


nai*_*tor 5

怎么样:

from dataclasses import dataclass


class Base:
    def __init__(self, a=1):
        self.a = a


@dataclass
class Child(Base):

    def __post_init__(self):
        super().__init__()


ch = Child()
Run Code Online (Sandbox Code Playgroud)


Arn*_*rne 5

在最佳实践中 [...],当我们进行继承时,应该首先调用初始化。

这是一个合理的最佳实践,但在数据类的特殊情况下,它没有任何意义。

调用父构造函数有两个原因,1) 实例化要由父构造函数处理的参数,以及 2) 运行父构造函数中需要在实例化之前发生的任何逻辑。

数据类已经为我们处理了第一个:

 @dataclass
class A:
    var_1: str

@dataclass
class B(A):
    var_2: str

print(B(var_1='a', var_2='b'))  # prints: B(var_1='a', var_2='b')
# 'var_a' got handled without us needing to do anything
Run Code Online (Sandbox Code Playgroud)

第二个不适用于数据类。其他类可能在它们的构造函数中做各种奇怪的事情,但数据类只做一件事:它们将输入参数分配给它们的属性。如果他们需要做任何其他事情(不能由 a 处理__post_init__),您可能正在编写一个不应该是数据类的类。