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__。我已经阅读了该文档,但没有看到与该概念有任何关系。我错过了什么吗?
实际上有一种方法是在之前调用的__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)
怎么样:
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)
在最佳实践中 [...],当我们进行继承时,应该首先调用初始化。
这是一个合理的最佳实践,但在数据类的特殊情况下,它没有任何意义。
调用父构造函数有两个原因,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__),您可能正在编写一个不应该是数据类的类。