我所知道的
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__
的概念来解决。我已经阅读了该文档,并且在那里没有看到与该概念有关的任何内容。我错过了什么吗?
答案 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()