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

时间:2019-02-28 14:19:02

标签: python inheritance python-dataclasses

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

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

我在做什么
由于数据类是在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__() 

我的问题
问题是我们必须将超级初始化调用放入__post_init__内,而实际上将其称为 数据类的 init 之后。
不利之处在于我们失去了召集合同,并且初始化混乱导致我们无法覆盖超类的属性。

可以用__pre_init__的概念来解决。我已经阅读了该文档,并且在那里没有看到与该概念有关的任何内容。我错过了什么吗?

3 个答案:

答案 0 :(得分:1)

实际上,有一种方法在__init__之前被调用:它是__new__。因此,您可以执行以下技巧:在Base.__init__中调用Child.__new__。我不能说这是一个很好的解决方案,但是如果您对此感到迷惑,则可以使用以下示例:

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


@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

答案 1 :(得分:1)

  

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

这是遵循的合理最佳实践,但是在特定的数据类情况下,这没有任何意义。

调用父级构造函数有两个原因: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

第二个不适用于数据类。其他类可能在其构造函数中执行各种奇怪的操作,但数据类仅执行一件事:它们将输入参数分配给其属性。如果他们需要做其他事情(__post_init__无法处理),则您可能正在编写一个不应作为数据类的类。

答案 2 :(得分:0)

怎么样:

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()