简体   繁体   English

你如何混合旧式和新式Python类?

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

I've seen a few questions on this topic, but I haven't been able to find a definitive answer. 我已经看过关于这个主题的几个问题,但我还没有找到明确的答案。

I would like to know the proper way to use old-style classes in a new Python code base. 我想知道在新的Python代码库中使用旧式类的正确方法。 Let's say for example that I have two fixed classes, A and B . 比方说,我有两个固定的类, AB. If I want to subclass A and B , and convert to new-style classes ( A2 and B2 ), this works. 如果我想要子类AB ,并转换为新式类( A2B2 ),这是有效的。 However there is an issue if I want to create a new class C , from A2 and B2 . 但是,如果我想从A2B2创建新的C类,则会出现问题。

Therefore, is it possible to continue with this method, or do all classes have to conform to the old-style if any base class is defined as old-style? 因此,是否可以继续使用此方法,或者如果任何基类被定义为旧式,那么所有类都必须符合旧式吗?

See the example code for clarification: 请参阅示例代码以获得说明:

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()

The output of this code: 这段代码的输出:

class A
class A2
---
class B
class B2
---
class A
class A2
class C

As you can see, the problem is that in the call to C() , class B2 was never initialized. 正如您所看到的,问题是在调用C() ,类B2从未初始化。


Update - New-Style Class Example 更新 - 新样式类示例

I guess it is not clear what the correct initialization sequence should be when using super . 我想还不清楚使用super时正确的初始化序列应该是什么。 Here is a working example where a call to super does initialize all base classes, not just the first one it finds . 这是一个工作示例,其中对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()

and produces the output: 并产生输出:

class B
class B2
class A
class A2
class C

This is not a issue of mixing old and new style classes. 这不是混合新旧类型的问题。 super() does not call all base classes functions, it calls the first one it finds according the method resolution order. super()不调用所有基类函数,它根据方法解析顺序调用它找到的第一个函数。 In this case A2, which in turn calls A. 在这种情况下,A2反过来调用A.

If you want to call both, do so explicitly: 如果要同时调用它们,请明确地执行此操作:

class C(A2, B2):
   def __init__(self):
      A2.__init__(self)
      B2.__init__(self)
      print 'class C'

That should solve it. 那应该解决它。

Update: 更新:

The diamond inheritance problem as you refer to, is the question of which class to call in a diamond inheritance situation, like this: 你所指的钻石继承问题是钻石继承情况下要调用哪个类的问题,如下所示:

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

Now test this out: 现在测试一下:

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

This is correct. 这是对的。 It calls the first class' implementation. 它调用了第一类的实现。 However, let's try this with method2: 但是,让我们尝试使用method2:

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

Oups, WRONG! Oups,错了! It should have called class C.method2() here, because even though class B does not override method2, class C does. 它应该在这里调用类C.method2(),因为即使类B不重写method2,类C也可以。 Now make class A a newstyle class: 现在让A类成为一个新类:

class A(object):
   def method1(self):
      print 'class A'

And try again: 然后再试一次:

>>> D().method1()
'class B'
>>> D().method2()
'class C'

and hey presto, it works. 嘿presto,它的工作原理。 This is the method resolution order difference between new and old-style classes, and this is what sometimes makes it confusing to mix them. 这是新旧类的方法解析顺序差异,这有时会使它们混淆。

Notice how at no point both B and C gets called. 注意B和C都不会被调用。 This is true even if we call 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'

If you want to call both B and C, you MUST call both explicitly. 如果你想同时调用B和C,你必须明确地调用它们。

Now if you unbreak the diamond, like in your example having separate base classes, the result is different: 现在,如果你破坏钻石,就像在你的例子中有单独的基类一样,结果是不同的:

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'

This is also per design. 这也是每个设计。 Still nowhere two base classes gets called. 仍然无法调用两个基类。 If you want that to happen you still have to call both explicitly. 如果你想要这样做,你仍然必须明确地调用它们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM