Python:在多次继承后查询类的父类(“super()”无效)
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 个回答
你不能不进行框架检查就做到这一点,这种做法是不被赞成的。
这里有一个我在网上找到的实现,它可以进行框架检查:
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)
你想要的是 __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 是如何处理多个继承的关系,还是挺有意思的。
正如上面提到的,我想获取“当前类的父类”。
但是你知道,在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__
。