Anton Ovsyannikov
Anton Ovsyannikov

Reputation: 1200

Python: how to call multiple super __new__ with multiple inheritance?

I have 2 classes: A and B, both of them instantiated by __new__ with different set of arguments (like a for A, and foo, bar for B). Now I want to implement class C, inherited from A and B, and instantiate it with 3 args: a, foo, bar, passing correspondent arguments to super classes __new__, but things goes wrong from here.

If we have no arguments I just call super(C, cls).__new__() and object of class C successfully created (it calls both A.__new__() and B.__new__() and combines it somehow). But how to do it 'by hands'? So I want to pass a to A.__new__, foo, bar to B.__new__ and combine somehow returned instances (is this right way to get object of class C at the end?).

Anyway I can't do both.

Fist - calling A.__new__ raises incorrect number of arguments exception in o = super(A, cls).__new__(cls) in A.__new__() (but A can be instantiated as standalone)

Second - I have no idea how to combine even successfully instantiated object of classes A and B into object of class C.

So could please someone to explain what is going on here?

class A(object):
    def __new__(cls, a):
        o = super(A, cls).__new__(cls)
        setattr(o, 'a', a)
        return o

class B(object):
    def __new__(cls, foo, bar):
        o = super(B, cls).__new__(cls)
        setattr(o, 'foo', foo)
        setattr(o, 'bar', bar)
        return o

print A(1) # ok, <__main__.A object at 0x00000000022F1630>
print B(2,3) # ok, <__main__.B object at 0x00000000022F1630>


class C(A,B):
    def __new__(cls, a, foo, bar):
        o1 = A.__new__(cls, a) #fail - exception while calling super.new in A
        o2 = B.__new__(cls, foo, bar)  #fail - exception while calling super.new in A
        # return # What? How to combine o1 o2 even if they are created succesfuly?
        # # return super(C, cls).__new__(cls, ?????)

print C(1,2,3)

Upvotes: 2

Views: 1530

Answers (2)

Jay Yang
Jay Yang

Reputation: 422

Just do return super().__new__(cls) in __new__ of derived class.

See following code snippet:

class A:
    def __new__(cls, *args, **kwargs):
        print('A new')
        return super().__new__(cls)
        
class B:
    def __new__(cls, *args, **kwargs):
        print('B new')
        return super().__new__(cls)  
        
class C(A, B):
    def __new__(cls, *args, **kwargs):
        print('D new')
        return super().__new__(cls)
        
        
c = C()
print([isinstance(c, cls) for cls in [A, B, C]])

Output:

D new
A new
B new
[True, True, True]

Upvotes: 0

Olivier Melan&#231;on
Olivier Melan&#231;on

Reputation: 22324

The method __new__ is what creates your instance, you should not call super(...).__new__ multiple times as it would create multiple instances.

What you want to do it use __init__ which initializes your already created instance.

class A(object):
    def __init__(self, a):
        self.a = a

class B(object):
    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar

class C(A, B):
    def __init__(self, a, foo, bar):
        A.__init__(self, a)
        B.__init__(self, foo, bar)

In particular, I want to point out that it is not true that on multiple inheritance Python will call both A.__new__ andB.__new__ and "combine somehow". Have a look at this code

class A(object):
    def __new__(*args):
        print('A.__new__ was called')
        return type(*args) # This is what ultimately creates every object in Python

class B(object):
    def __new__(*args):
        print('B.__new__ was called')
        return type(*args)

# As expected the following is printed when instances are created
a = A() # Prints 'A.__new__ was called'
b = B() # Prints 'B.__new__ was called'

class C(A, B):
    pass

c = C() # Prints 'A.__new__ was called'

So we observe that B.__new__ was never called. On multiple inheritance, Python will inherit the method from the left-most class that has this method. In this case, C inherited A.__new__.

Upvotes: 3

Related Questions