使用python数据类实现多重继承 [英] Achieving multiple inheritance using python dataclasses

查看:148
本文介绍了使用python数据类实现多重继承的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用新的 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆