当 Python 数据 class 继承 class 时控制初始化顺序
control initialize order when Python dataclass inheriting a class
我所知道的
Python 数据class 允许继承,数据class 或class。
在最佳实践中(以及其他语言),当我们进行继承时,应该首先调用初始化。在 Python 中是:
def __init__(self):
super().__init__()
...
我在做什么
由于 dataclass 是在 Python 3.7 中引入的,我正在考虑用 dataclass 替换我所有的 classes。
使用数据class,它的好处之一是为您生成__init__
。当 dataclass 需要继承基数 class 时,这并不好——例如:
class Base:
def __init__(self):
self.a = 1
@dataclass
class Child(Base):
a:int
def __post_init__(self):
super().__init__()
我的问题
问题是我们必须将超级初始化调用放在 __post_init__
中,实际上调用 after dataclass 的 init.
缺点是我们失去了约定契约,初始化混乱导致我们无法覆盖 super classes.
的属性。
可以通过__pre_init__
的概念来解决。我已经阅读了该文档,但没有发现与该概念有任何关系。我错过了什么吗?
怎么样:
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()
实际上在__init__
之前调用了一种方法:它是__new__
。所以你可以做这样一个技巧:在Child.__new__
中调用Base.__init__
。我不能说这是一个好的解决方案,但如果您有兴趣,这里有一个工作示例:
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
In best practice [...], when we do inheritance, the initialization should be called first.
这是一个合理的最佳实践,但在数据classes 的特定情况下,它没有任何意义。
调用父构造函数有两个原因,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
而第二个不适用于数据classes。其他 classes 可能会在它们的构造函数中做各种奇怪的事情,但 dataclasses 只做一件事:它们将输入参数分配给它们的属性。如果他们需要做任何其他事情(不能由 __post_init__
处理),你可能正在写一个 class 不应该是数据 class.
我所知道的
Python 数据class 允许继承,数据class 或class。
在最佳实践中(以及其他语言),当我们进行继承时,应该首先调用初始化。在 Python 中是:
def __init__(self):
super().__init__()
...
我在做什么
由于 dataclass 是在 Python 3.7 中引入的,我正在考虑用 dataclass 替换我所有的 classes。
使用数据class,它的好处之一是为您生成__init__
。当 dataclass 需要继承基数 class 时,这并不好——例如:
class Base:
def __init__(self):
self.a = 1
@dataclass
class Child(Base):
a:int
def __post_init__(self):
super().__init__()
我的问题
问题是我们必须将超级初始化调用放在 __post_init__
中,实际上调用 after dataclass 的 init.
缺点是我们失去了约定契约,初始化混乱导致我们无法覆盖 super classes.
可以通过__pre_init__
的概念来解决。我已经阅读了该文档,但没有发现与该概念有任何关系。我错过了什么吗?
怎么样:
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()
实际上在__init__
之前调用了一种方法:它是__new__
。所以你可以做这样一个技巧:在Child.__new__
中调用Base.__init__
。我不能说这是一个好的解决方案,但如果您有兴趣,这里有一个工作示例:
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
In best practice [...], when we do inheritance, the initialization should be called first.
这是一个合理的最佳实践,但在数据classes 的特定情况下,它没有任何意义。
调用父构造函数有两个原因,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
而第二个不适用于数据classes。其他 classes 可能会在它们的构造函数中做各种奇怪的事情,但 dataclasses 只做一件事:它们将输入参数分配给它们的属性。如果他们需要做任何其他事情(不能由 __post_init__
处理),你可能正在写一个 class 不应该是数据 class.