使用python数据类实现多重继承 [英] Achieving multiple inheritance using python dataclasses
问题描述
我正在尝试使用新的 python 数据类来创建一些混合类(在我写这篇文章时,我认为这听起来像是一个轻率的想法),但我遇到了一些问题.看下面的例子:
I'm trying to use the new python dataclasses to create some mix-in classes (already as I write this I think it sounds like a rash idea), and I'm having some issues. Behold the example below:
从数据类导入数据类
@dataclass
class NamedObj:
name: str
def __post_init__(self):
print("NamedObj __post_init__")
self.name = "Name: " + self.name
@dataclass
class NumberedObj:
number: int = 0
def __post_init__(self):
print("NumberedObj __post_init__")
self.number += 1
@dataclass
class NamedAndNumbered(NumberedObj, NamedObj):
def __post_init__(self):
super().__post_init__()
print("NamedAndNumbered __post_init__")
如果我再尝试:
nandn = NamedAndNumbered('n_and_n')
print(nandn.name)
print(nandn.number)
我明白
NumberedObj __post_init__
NamedAndNumbered __post_init__
n_and_n
1
建议它已经为 NamedObj
运行了 __post_init__
,但没有为 NumberedObj
运行.我想要的是让 NamedAndNumbered 为它的两个混合类 Named 和 Numbered 运行 __post_init__
.有人可能认为如果 NamedAndNumbered
有一个像这样的 __post_init__
就可以做到:
Suggesting it has run __post_init__
for NamedObj
, but not for NumberedObj
.
What I would like is to have NamedAndNumbered run __post_init__
for both of its mix-in classes, Named and Numbered. One might think that it could be done if NamedAndNumbered
had a __post_init__
like this:
def __post_init__(self):
super(NamedObj, self).__post_init__()
super(NumberedObj, self).__post_init__()
print("NamedAndNumbered __post_init__")
但这只是给我一个错误 AttributeError: 'super' object has no attribute '__post_init__'
当我尝试调用 NamedObj.__post_init__()
时.
But this just gives me an error AttributeError: 'super' object has no attribute '__post_init__'
when I try to call NamedObj.__post_init__()
.
在这一点上,我不完全确定这是数据类的错误/功能,还是与我对 Python 继承方法的可能有缺陷的理解有关.有人可以帮忙吗?
At this point I'm not entirely sure if this is a bug/feature with dataclasses or something to do with my probably-flawed understanding of Python's approach to inheritance. Could anyone lend a hand?
推荐答案
这个:
def __post_init__(self):
super(NamedObj, self).__post_init__()
super(NumberedObj, self).__post_init__()
print("NamedAndNumbered __post_init__")
不会做你认为它会做的事情.super(cls, obj)
将在 type(obj).__mro__
中返回一个代理给 after cls
类- 所以,在你的情况下,object
.协作 super()
调用的重点是避免必须显式调用每个父对象.
doesn't do what you think it does. super(cls, obj)
will return a proxy to the class after cls
in type(obj).__mro__
- so, in your case, to object
. And the whole point of cooperative super()
calls is to avoid having to explicitely call each of the parents.
协作 super()
调用旨在工作的方式是,通过协作" - IOW,mro 中的每个人都应该将调用中继到下一个类(实际上,super
名称是一个相当可悲的选择,因为它不是关于调用超类",而是关于调用 mro 中的下一个类").
The way cooperative super()
calls are intended to work is, well, by being "cooperative" - IOW, everyone in the mro is supposed to relay the call to the next class (actually, the super
name is a rather sad choice, as it's not about calling "the super class", but about "calling the next class in the mro").
IOW,您希望您的每个可组合"数据类(它们不是 mixins - mixins 仅具有行为)来中继调用,因此您可以按任何顺序组合它们.第一个简单的实现看起来像:
IOW, you want each of your "composable" dataclasses (which are not mixins - mixins only have behaviour) to relay the call, so you can compose them in any order. A first naive implementation would look like:
@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__
方法.解决方案很简单:只需添加一个定义此方法为 noop 的基类,并使所有可组合的数据类都继承自它:
BUT this doesn't work, since for the last class in the mro (here NamedObj
), the next class in the mro is the builtin object
class, which doesn't have a __post_init__
method. The solution is simple: just add a base class that defines this method as a noop, and make all your composable dataclasses inherit from it:
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__")
这篇关于使用python数据类实现多重继承的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!