Python:在多次继承后查询类的父类(“super()”无效)

-2 投票
3 回答
775 浏览
提问于 2025-04-16 09:42
currentParentClass().__init__()
...
currentParentClass().__init__()
...
currentParentClass().__init__()

我建立了一个类系统,使用了多个从基础类派生的类(对象->类1->类2->类3):

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

class class2(class1):
 def __init__(self):
  print "class2.__init__()"
  class1.__init__(self)


class class3(class2):
 def __init__(self):
  print "class3.__init__()"
  class2.__init__(self)

x = class3()

它按预期工作并打印出:

class3.__init__()
class2.__init__()
class1.__init__()

现在我想把这三行代码替换成:

object.__init__(self)
...
class1.__init__(self)
...
class2.__init__(self)

基本上,我想创建一个类系统,这样我就不需要每次都输入“classXYZ.doSomething()”。

正如上面提到的,我想获取“当前类的父类”。

用下面的代码替换这三行:

super(type(self), self).__init__()

但是这样不行(它总是返回当前实例的父类 -> 类2),会导致无限循环打印:

class3.__init__()
class2.__init__()
class2.__init__()
class2.__init__()
class2.__init__()
...

所以有没有什么方法可以让我获取当前类的父类呢?

谢谢你的帮助!

亨利

--------------------

编辑:

@Lennart,可能我理解错了,但我觉得我没有清楚地描述问题。所以这个例子可能能更好地解释:

让我们创建另一个子类:

class class4(class3):
 pass

那么如果我们从类4派生一个实例,会发生什么呢?

y = class4()

我觉得它明显执行了:

super(class3, self).__init__()

我们可以把它翻译成:

class2.__init__(y)

这显然不是目标(那应该是 class3.__init__(y)

现在调用很多父类的函数 - 我不想在我的 super() 调用中重新实现所有的函数,使用不同的基础类名称。

我还想提到,我对 Python 的类系统有点陌生,所以希望你们能耐心点。

3 个回答

-4

你不能不进行框架检查就做到这一点,这种做法是不被赞成的。

这里有一个我在网上找到的实现,它可以进行框架检查:

import sys

class _super(object):
    __name__ = 'super'

    __slots__ = ('_super__super', '_super__method')

    def __init__(self, super, name):
        object.__setattr__(self, '_super__super', super)

        try:
            object.__setattr__(self, '_super__method', getattr(super, name))
        except AttributeError:
            object.__setattr__(self, '_super__method', name)

    def __call__(self, *p, **kw):
        method = object.__getattribute__(self, '_super__method')

        try:
            return method(*p, **kw)
        except TypeError:
            if type(method) is not str:
                raise

        super = object.__getattribute__(self, '_super__super')
        method = getattr(super, method)
        object.__setattr__(self, '_super__method', method)
        return method(*p, **kw)

    def __getattribute__ (self, name):
        super = object.__getattribute__(self, '_super__super')

        try:
            return getattr(super, name)
        except (TypeError, AttributeError):
            if name == 'super':
                raise TypeError("Cannot get 'super' object of 'super' object")

            raise

    def __setattr__(self, name, value):
        super = object.__getattribute__(self, '_super__super')
        object.__setattr__(super, name, value)

def _getSuper(self):
    frame = sys._getframe().f_back
    code = frame.f_code
    name = code.co_name

    cur_class = None

    for c in type(self).__mro__:
        try:
            m = getattr(c, name)
            func_code = m.func_code
        except AttributeError:
            func_code = None

        if func_code is code:
            cur_class = c
        elif cur_class is not None:
            break

    if cur_class is None:
        raise TypeError, "Can only call 'super' in a bound method"

    return _super(super(cur_class, self), name)

class autosuper(object):
    __slots__ = ()
    super = property(_getSuper)

你在使用它时的代码:

class class1(autosuper):
    def __init__(self):
        print "class1.__init__()"
        self.super()

class class2(class1):
    def __init__(self):
        print "class2.__init__()"
        self.super()


class class3(class2):
    def __init__(self):
        print "class3.__init__()"
        self.super()

x = class3()        

python3中,super() 已经很神奇,可以在不传递类的情况下工作:

super().method(args)
0

你想要的是 __class____bases__ 成员。你可以这样获取你类的父类:

>>> class A: pass

>>> class B(A): pass

>>> class C(B): pass

>>> C.__bases__[0]
<class __main__.B at ...>

>>> C.__bases__[0].__bases__[0]
<class __main__.A at ...>

不过,要记住,你的类可以有多个父类。

>>> class D: pass

>>> class E(A,D): pass

>>> E.__bases__
(<class __main__.A at ...>, <class __main__.D at ...>)

在我看来,你所做的事情不常见,但了解 Python 是如何处理多个继承的关系,还是挺有意思的。

4

正如上面提到的,我想获取“当前类的父类”。

但是你知道,在class2中,它的父类是class1。所以你想一直调用class1。这样的话,你可以直接调用class1。

但获取当前类的父类的正确方法是用super()。不过你是这样使用的:

super(type(self), self)

这样做会得到self的类的父类。在你的例子中,self的类是class3,所以正如你所说,它总是返回class2。正确的使用方法如下:

class class1(object):
 def __init__(self):
  print "class1.__init__()"
  super(class1, self).__init__()

class class2(class1):
 def __init__(self):
  print "class2.__init__()"
  super(class2, self).__init__()


class class3(class2):
 def __init__(self):
  print "class3.__init__()"
  super(class3, self).__init__()


x = class3()

使用super()和直接调用类的区别在于,如果你在某个层级中使用了多重继承,super会自动找出正确的类。


更新:

当你定义了class4并调用它时,会发生什么呢?

>>> class class4(class3):
...  pass
... 
>>> y = class4()
class3.__init__()
class2.__init__()
class1.__init__()

正如你所说,目标是调用class3.__init__()。这正是发生的事情,因为class4会根据正常的继承规则从class3继承__init__

你期待class2.__init__()被调用,但我不明白你为什么会有这样的期待。你是从class3继承的,所以会调用class3的__init__。而class3又会根据设计调用class2.__init__

撰写回答