简体   繁体   中英

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. Let's say for example that I have two fixed classes, A and B . If I want to subclass A and B , and convert to new-style classes ( A2 and B2 ), this works. However there is an issue if I want to create a new class C , from A2 and B2 .

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.


Update - New-Style Class Example

I guess it is not clear what the correct initialization sequence should be when using super . Here is a working example where a call to super does initialize all base classes, not just the first one it finds .

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. In this case A2, which in turn calls 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:

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

Oups, WRONG! It should have called class C.method2() here, because even though class B does not override method2, class C does. Now make class A a newstyle class:

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. 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. 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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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