如何混合旧式与新式Python类?
我看到有一些关于这个话题的问题,但我找不到一个明确的答案。
我想知道在新的Python代码中,如何正确使用旧式类。比如说,我有两个固定的类,A和B。如果我想从A和B派生出新的类,并把它们转换成新式类(A2和B2),这样是可以的。但是,如果我想从A2和B2创建一个新的类C,就会出现问题。
所以,我想知道是否可以继续使用这种方法,还是说如果有任何基类被定义为旧式类,那么所有的类都必须遵循旧式的规则呢?
请看下面的示例代码以便更清楚:
class A:
def __init__(self):
print 'class A'
class B:
def __init__(self):
print 'class B'
class A2(A,object):
def __init__(self):
super(A2, self).__init__()
print 'class A2'
class B2(B,object):
def __init__(self):
super(B2, self).__init__()
print 'class B2'
class C(A2, B2):
def __init__(self):
super(C,self).__init__()
print 'class C'
A2()
print '---'
B2()
print '---'
C()
这段代码的输出结果是:
class A
class A2
---
class B
class B2
---
class A
class A2
class C
正如你所看到的,问题在于在调用 C()
时,类B2并没有被初始化。
更新 - 新式类示例
我想可能不太清楚在使用 super
时,正确的初始化顺序应该是什么。这里有一个有效的示例,调用super确实会初始化所有基类,而不仅仅是找到的第一个。
class A(object):
def __init__(self):
super(A, self).__init__()
print 'class A'
class B(object):
def __init__(self):
super(B, self).__init__()
print 'class B'
class A2(A):
def __init__(self):
super(A2, self).__init__()
print 'class A2'
class B2(B):
def __init__(self):
super(B2, self).__init__()
print 'class B2'
class C(A2, B2):
def __init__(self):
super(C, self).__init__()
print 'class C'
C()
并产生以下输出:
class B
class B2
class A
class A2
class C
1 个回答
这不是老式和新式类混用的问题。super() 并不会调用所有基类的函数,它只会根据方法解析顺序调用找到的第一个。在这个例子中,是 A2,它又调用了 A。
如果你想同时调用两个类的函数,就要明确地调用它们:
class C(A2, B2):
def __init__(self):
A2.__init__(self)
B2.__init__(self)
print 'class C'
这样就可以解决问题了。
更新:
你提到的菱形继承问题,就是在菱形继承的情况下,应该调用哪个类,比如这样:
class A:
def method1(self):
print 'class A'
def method2(self):
print 'class A'
class B(A):
def method1(self):
print 'class B'
class C(A):
def method1(self):
print 'class C'
def method2(self):
print 'class C'
class D(B, C):
pass
现在试试这个:
>>> D().method1()
'class B'
这是正确的。它调用了第一个类的实现。不过,让我们用 method2 再试一次:
>>> D().method2()
'class A'
哎呀,错了!这里应该调用 class C.method2(),因为虽然 class B 没有重写 method2,但 class C 重写了。现在把 class A 改成新式类:
class A(object):
def method1(self):
print 'class A'
再试一次:
>>> D().method1()
'class B'
>>> D().method2()
'class C'
看,成功了。这就是新式和老式类之间方法解析顺序的不同,这也是为什么混用时会让人感到困惑。
注意,在任何时候,B 和 C 都没有被同时调用。即使我们调用了 super 也是如此。
class D(B, C):
def method1(self):
super(D, self).method1()
def method2(self):
super(D, self).method2()
>>> D().method1()
'class B'
>>> D().method2()
'class C'
如果你想同时调用 B 和 C,必须明确地调用它们。
现在如果你打破菱形结构,就像你例子中有独立的基类,结果会不同:
class A1(object):
def method1(self):
print 'class A1'
def method2(self):
print 'class A1'
class A2(object):
def method1(self):
print 'class A2'
def method2(self):
print 'class A2'
class B(A1):
def method1(self):
print 'class B'
class C(A2):
def method1(self):
print 'class C'
def method2(self):
print 'class C'
class D(B, C):
def method1(self):
super(D, self).method1()
def method2(self):
super(D, self).method2()
>>> D().method1()
'class B'
>>> D().method2()
'class A1'
这也是设计使然。依然没有两个基类被同时调用。如果你想让它们都被调用,还是得明确地调用它们。