Это:
def __post_init__(self):
super(NamedObj, self).__post_init__()
super(NumberedObj, self).__post_init__()
print("NamedAndNumbered __post_init__")
не делает то, что вы думаете, что делает. super(cls, obj)
вернет прокси классу после cls
в type(obj).__mro__
- так, в вашем случае, до object
. И весь смысл кооперативных super()
звонков состоит в том, чтобы избежать необходимости явного звонка каждому из родителей.
Способ, которым кооперативные super()
звонки предназначены для работы, это, ну, в общем, быть "кооперативным" - IOW все в mro должны передавать вызов следующему классу (на самом деле имя super
довольно печальный выбор, поскольку речь идет не о вызове «суперкласса», а о «вызове следующего класса в mro»). «).
Итак, вы хотите, чтобы каждый из ваших «компонуемых» классов данных (которые не являются миксинами - только миксины имеют поведение) передавал вызов, поэтому вы можете создавать их в любом порядке. Первая наивная реализация будет выглядеть так:
@dataclass
class NamedObj:
name: str
def __post_init__(self):
super().__post_init__()
print("NamedObj __post_init__")
self.name = "Name: " + self.name
@dataclass
class NumberedObj:
number: int = 0
def __post_init__(self):
super().__post_init__()
print("NumberedObj __post_init__")
self.number += 1
@dataclass
class NamedAndNumbered(NumberedObj, NamedObj):
def __post_init__(self):
super().__post_init__()
print("NamedAndNumbered __post_init__")
НО это не работает, поскольку для последнего класса в mro (здесь NamedObj
) следующим классом в mro является встроенный object
класс, который не имеет __post_init__
метода. Решение простое: просто добавьте базовый класс, который определяет этот метод как oop, и сделайте так, чтобы все ваши составные классы данных наследовали от него:
class Base(object):
def __post_init__(self):
# just intercept the __post_init__ calls so they
# aren't relayed to `object`
pass
@dataclass
class NamedObj(Base):
name: str
def __post_init__(self):
super().__post_init__()
print("NamedObj __post_init__")
self.name = "Name: " + self.name
@dataclass
class NumberedObj:
number: int = 0
def __post_init__(self):
super().__post_init__()
print("NumberedObj __post_init__")
self.number += 1
@dataclass
class NamedAndNumbered(NumberedObj, NamedObj):
def __post_init__(self):
super().__post_init__()
print("NamedAndNumbered __post_init__")