基类具有不同的init参数时的方法解析顺序

2024-09-22 20:39:30 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图理解Python中的MRO。虽然这里有各种各样的帖子,但我并没有特别得到我想要的东西。考虑从BaseClass派生的两个类A和{},每个类都有一个采用不同参数的{}。在

class BaseClass(object):
    def __init__(self):
        print "I am the base class"

class A(BaseClass):
    def __init__(self, something, anotherthing):
        super(A, self).__init__()
        self.something = something
        self.anotherthing = anotherthing
    def methodsA(self):
        ...

class B(BaseClass):
    def __init__(self, someOtherThing):
        super(B, self).__init__()
        self.someOtherThing = someOtherThing 
    def methodsB(self):
        ...

问题是,如果我需要从AB派生第三个类C,如果必须的话,如何初始化{}?我可以安全地从B或{}导出{}。在

^{pr2}$

上面的实现给了我一个错误。在


Tags: self参数objectinitdefamsomething帖子
3条回答

要理解这一点,请不要争论:

class BaseClass(object):
    def __init__(self):
        print("BaseClass.__init__")

class A(BaseClass):
    def __init__(self):
        print("A.__init__")
        super(A, self).__init__()

class B(BaseClass):
    def __init__(self):
        print("B.__init__")
        super(B, self).__init__()

class C(A, B):
    def __init__(self):
        print("C.__init__")
        super(C, self).__init__()

当我们运行这个程序时:

^{pr2}$

这就是super所做的:它确保所有的调用都按正确的顺序进行,没有重复。C继承自A和{},因此它们的{}方法都应该被调用,而且它们都继承自{},因此{}也应该被调用,但只能调用一次。

如果被调用的__init__方法采用不同的参数,并且不能处理额外的参数(例如*args, **kwargs),那么您将得到您所引用的TypeError。要解决这个问题,需要确保所有方法都可以处理适当的参数。

正如jornsharpe在他的文章末尾提到的,我遇到的最好的方法 处理这种情况的方法是接受**kwargs并提取 显式命名参数。

class BaseClass(object):
    def __init__(self, **kwargs):
        print("BaseClass.__init__({},{})".format('', kwargs))
        super(BaseClass,self).__init__(**kwargs)            

class A(BaseClass):
    def __init__(self, **kwargs):
        print("A.__init__({},{})".format('', kwargs))

        a = kwargs.pop('something')
        super(A,self).__init__(**kwargs)

class B(BaseClass):
    def __init__(self, **kwargs):
        print("B.__init__({},{})".format('', kwargs))       

        b = kwargs.pop('anotherthing')
        super(B,self).__init__(**kwargs)


class C(A, B):
    def __init__(self, **kwargs):
        print("C.__init__({},{})".format('', kwargs))

        super(C,self).__init__(**kwargs)


c = C(something=1,anotherthing='a')

需要提取的参数应该传入named,因此它们以kwargs显示。

您甚至可以通过将*args作为示例中的注释来显式地只接受命名参数,这样如果您忘记了,就会发现自己有一个TypeError。

编辑:

经过一段时间的思考,我意识到我的例子是非常具体的,如果你引入另一个类或改变继承,它可能会崩溃。有两件事应该解决,以使这一点更普遍:

基类不调用super

在这个例子中,这并不重要,但是如果引入另一个类,MRO可能会改变,在基类之后有一个类,因此它应该调用super。这就引出了第二个问题:

object.__init__()不带参数

如果我们想让类(特别是BaseClass)安全地放入一个通用的多重继承结构中,在这个结构中,它的超级调用可能被分派到另一个类对象,那么我们需要在使用它们时将参数从kwargs中弹出。

{cd4>在这个名字里加上了两个相同的复杂度,但不需要。我想关键是让多重继承以一般的方式工作是困难的。

以下是一篇有趣的文章(通过google找到)关于一些细节:article

我相信你不能用super来做这个。你必须使用“旧风格”:

class C(A,B):
    def __init__(self, something, anotherthing, someOtherThing):
        A.__init__(self, something, anotherthing)
        B.__init__(self, someOtherThing)

相关问题 更多 >