Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
like image 752
cmcginty Avatar asked Sep 15 '09 07:09

cmcginty


People also ask

What is an old-style class in Python?

Up to Python 2.1, old-style classes were the only flavour available to the user. The concept of (old-style) class is unrelated to the concept of type: if x is an instance of an old-style class, then x.__class__ designates the class of x, but type (x) is always <type 'instance'>.

What is the difference between old and new style classes?

New-style classes are created by specifying another new-style class (i.e. a type) as a parent class, or the "top-level type" object if no other parent is needed. The behaviour of new-style classes differs from that of old-style classes in a number of important details in addition to what type returns.

What is a new-style class in Python?

New-style classes were introduced in Python 2.2 to unify classes and types. They inherit from the top-level object type. A new-style class is a user-defined type, and is very similar to built-in types.

Is it possible to subclass an object in Python 3?

Python 3 only has new-style classes. No matter if you subclass from object or not, classes are new-style in Python 3. Show activity on this post. New-style classes inherit from object, or from another new-style class. Old-style classes don't. Python 3 doesn't support old style classes, so either form noted above results in a new-style class.


1 Answers

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.

like image 133
Lennart Regebro Avatar answered Oct 26 '22 08:10

Lennart Regebro