关于类:如何混合旧样式和新样式的Python类?

How do you mix old-style and new-style Python classes?

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

我想知道在新的Python代码库中使用旧样式类的正确方法。例如,我有两个固定类,A和B。如果我想子类A和B,并转换为新样式的类(A2和B2),这是可行的。但是,如果我想创建一个新的C类,从A2和B2有一个问题。

因此,是否可以继续使用此方法,或者如果将任何基类定义为旧样式,所有类都必须符合旧样式?

请参见示例代码进行说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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()

此代码的输出:

1
2
3
4
5
6
7
8
9
class A
class A2
---
class B
class B2
---
class A
class A2
class C

如您所见,问题是在对C()的调用中,类b2从未初始化。

更新-新样式类示例

我想在使用super时,不清楚正确的初始化顺序应该是什么。下面是一个工作示例,其中对super的调用会初始化所有的基类,而不仅仅是它找到的第一个基类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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()

并产生输出:

1
2
3
4
5
class B
class B2
class A
class A2
class C


这不是混合新旧样式类的问题。super()不调用所有的基类函数,它调用根据方法解析顺序找到的第一个函数。在这种情况下,A2又称A。

如果要同时调用这两者,请显式执行以下操作:

1
2
3
4
5
class C(A2, B2):
   def __init__(self):
      A2.__init__(self)
      B2.__init__(self)
      print 'class C'

这应该能解决问题。

更新:

正如您所提到的菱形继承问题,是在菱形继承情况下调用哪个类的问题,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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

现在测试一下:

1
2
>>> D().method1()
'class B'

这是正确的。它调用第一类的实现。不过,让我们用方法2来尝试一下:

1
2
>>> D().method2()
'class A'

哎呀,错了!它应该在这里调用类C.method2(),因为即使类B不重写method2,类C也会这样做。现在把A班变成一个新的风格班:

1
2
3
class A(object):
   def method1(self):
      print 'class A'

再试一次:

1
2
3
4
>>> D().method1()
'class B'
>>> D().method2()
'class C'

嘿,普雷斯托,它起作用了。这是新样式类和旧样式类之间的方法解析顺序差异,这有时会使混合它们变得混乱。

注意B和C在任何时候都不会被调用。这是真的,即使我们称之为超级。

1
2
3
4
5
6
7
8
9
10
11
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,则必须显式调用这两者。

现在,如果您像在示例中有单独的基类一样不破坏菱形,则结果是不同的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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'

这也是按设计的。仍然没有调用两个基类。如果您希望发生这种情况,您仍然必须明确地调用这两者。