Chr*_*per 4 python multiple-inheritance mixins method-resolution-order python-dataclasses
我正在尝试使用新的 python 数据类来创建一些混合类(在我写这篇文章时,我认为这听起来像是一个轻率的想法),但我遇到了一些问题。看下面的例子:
从数据类导入数据类
@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__")
Run Code Online (Sandbox Code Playgroud)
如果我再尝试:
nandn = NamedAndNumbered('n_and_n')
print(nandn.name)
print(nandn.number)
Run Code Online (Sandbox Code Playgroud)
我得到
NumberedObj __post_init__
NamedAndNumbered __post_init__
n_and_n
1
Run Code Online (Sandbox Code Playgroud)
暗示它已经运行__post_init__了NamedObj,但没有运行NumberedObj。我想要的是让 NamedAndNumbered__post_init__为它的两个混合类 Named 和 Numbered 运行。有人可能认为如果NamedAndNumbered有这样的就可以做到__post_init__:
def __post_init__(self):
super(NamedObj, self).__post_init__()
super(NumberedObj, self).__post_init__()
print("NamedAndNumbered __post_init__")
Run Code Online (Sandbox Code Playgroud)
但是AttributeError: 'super' object has no attribute '__post_init__'当我尝试调用NamedObj.__post_init__().
在这一点上,我不完全确定这是数据类的错误/功能还是与我对 Python 继承方法的可能有缺陷的理解有关。有人可以伸出援手吗?
该问题(很可能)与 es 无关dataclass。问题出在Python的方法解析上。调用方法 on会调用MROsuper()链中父类中第一个找到的方法。因此,要使其工作,您需要手动调用父类的方法:
@dataclass
class NamedAndNumbered(NumberedObj, NamedObj):
def __post_init__(self):
NamedObj.__post_init__(self)
NumberedObj.__post_init__(self)
print("NamedAndNumbered __post_init__")
Run Code Online (Sandbox Code Playgroud)
另一种方法(如果你真的喜欢super())可能是通过调用super()所有父类来继续 MRO 链(但它需要有一个__post_init__in 链):
@dataclass
class MixinObj:
def __post_init__(self):
pass
@dataclass
class NamedObj(MixinObj):
name: str
def __post_init__(self):
super().__post_init__()
print("NamedObj __post_init__")
self.name = "Name: " + self.name
@dataclass
class NumberedObj(MixinObj):
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__")
Run Code Online (Sandbox Code Playgroud)
在这两种方法中:
>>> nandn = NamedAndNumbered('n_and_n')
NamedObj __post_init__
NumberedObj __post_init__
NamedAndNumbered __post_init__
>>> print(nandn.name)
Name: n_and_n
>>> print(nandn.number)
1
Run Code Online (Sandbox Code Playgroud)
这个:
def __post_init__(self):
super(NamedObj, self).__post_init__()
super(NumberedObj, self).__post_init__()
print("NamedAndNumbered __post_init__")
Run Code Online (Sandbox Code Playgroud)
不会做你认为它会做的事情。super(cls, obj)将在 clsin之后返回一个类的代理type(obj).__mro__- 所以,在你的情况下,到object. 合作super()调用的全部意义在于避免必须明确地调用每个父母。
合作super()调用的工作方式是,通过“合作” - IOW,mro 中的每个人都应该将调用转发给下一个班级(实际上,这个super名字是一个相当可悲的选择,因为它不是关于调用“超级班级”,而是关于“在 mro 中调用下一个班级”)。
IOW,您希望您的每个“可组合”数据类(不是 mixins - mixins 只有行为)来中继调用,因此您可以按任何顺序组合它们。第一个简单的实现看起来像:
@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__")
Run Code Online (Sandbox Code Playgroud)
但这不起作用,因为对于 mro 中的最后一个类(此处NamedObj),mro 中的下一个类是内置object类,它没有__post_init__方法。解决方案很简单:只需添加一个将这个方法定义为 noop 的基类,并使所有可组合的数据类都继承自它:
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__")
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2267 次 |
| 最近记录: |