如何混合旧式与新式Python类?

8 投票
1 回答
4104 浏览
提问于 2025-04-15 14:20

我看到有一些关于这个话题的问题,但我找不到一个明确的答案。

我想知道在新的Python代码中,如何正确使用旧式类。比如说,我有两个固定的类,AB。如果我想从AB派生出新的类,并把它们转换成新式类(A2B2),这样是可以的。但是,如果我想从A2B2创建一个新的类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 个回答

7

这不是老式和新式类混用的问题。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'

这也是设计使然。依然没有两个基类被同时调用。如果你想让它们都被调用,还是得明确地调用它们。

撰写回答