16

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

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

我在做什么
由于数据类是在 Python 3.7 中引入的,我正在考虑用数据类替换我的所有类。使用数据类,它的好处之一就是__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__。我已经阅读了该文件,并没有看到与该概念有任何关系。我错过了什么吗?

4

3 回答 3

9

实际上,之前调用了一种方法__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
于 2019-02-28T20:06:27.920 回答
6

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

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

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

Dataclasses 已经为我们处理了第一个:

 @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

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

于 2019-03-11T07:39:56.537 回答
4

怎么样:

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()
于 2019-02-28T14:37:04.007 回答