Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling superclass constructors in python with different arguments

Tags:

class A():     def __init__( self, x, y):         self.x = x         self.y = y  class B():     def __init__( self, z=0):         self.z = z    class AB(A,B):     def __init__( self, x, y, z=0):         ? 

How can I make the constructor of AB call the constructors for A and B with the proper arguments?

I've tried

class AB(A,B):     def __init__( self, x, y, z=0):         A.__init__(x,y)         B.__init__(z) 

but this gives me an error.

like image 220
Alexander Gruber Avatar asked Aug 01 '13 23:08

Alexander Gruber


People also ask

How do I call a super constructor in Python?

Use __init()__ on super() inside the constructor of the subclass to invoke the constructor of the superclass in Python. In inheritance invoking the super constructor of a subclass invokes the constructor of its superclass.

Can we pass arguments to super () in Python?

A super() Deep Dive While the examples above (and below) call super() without any parameters, super() can also take two parameters: the first is the subclass, and the second parameter is an object that is an instance of that subclass.

What is super () __ Init__ in Python?

When you initialize a child class in Python, you can call the super(). __init__() method. This initializes the parent class object into the child class. In addition to this, you can add child-specific information to the child object as well.

How do you call the immediate superclass constructor in Python?

Use super(). __init()__ to call the immediate parent class constructor in Python. Calling a parent constructor within a child class executes the operations of the parent class constructor in the child class.


2 Answers

Other answers suggested adding self to the first parameter.

But usually invocations of __init__ in parent classes are made by super.

Consider this example:

class A(object):     def __init__(self, x):         print('__init__ is called in A')         self.x = x   class B(object):     def __init__(self, *args, **kwargs):         print('__init__ is called in B')         super(B, self).__init__(*args, **kwargs)   class AB(B, A):     def __init__(self, *args, **kwargs):         print('__init__ is called in AB')         super(AB, self).__init__(*args, **kwargs) 

AB class contains an order in which constructors and initializators should be called:

>>> AB.__mro__ (<class '__main__.AB'>, <class '__main__.B'>, <class '__main__.A'>, <type 'object'>) 

See, that first AB's __init__ is invoked, then B's, then A's, and then object's.

Let's check:

>>> ab = AB(1) __init__ is called in AB __init__ is called in B __init__ is called in A 

But these calls through this chain are made by super. When we type super(AB, self), it means: find then next class after AB in __mro__ chain of self.

Then we should invoke super in B, looking for the next class in the chain after B: super(B, self).

It's important to use super and not call manually A.__init__(self,...), etc., as it may lead to problems later. Read this for more info.

So, if you stick with super, then there is a problem. __init__ methods in your classes expect different parameters. And you can't know for sure the order in which super will be invoking methods in these classes. The order is determined by C3 algorithm at the time of class creation. In subclasses another classes may get in-between of the call chain. So you can't have different parameters in __init__, as in this case you will have always to consider all inheritance chain to understand how __init__ methods will be called.

For example, consider adding C(A) and D(B) classes and CD subclass of them. Then A will no longer be invoked after B, but after C.

class A(object):     def __init__(self, *args, **kwargs):         print('__init__ is called in A')         super(A, self).__init__(*args, **kwargs)   class B(object):     def __init__(self, *args, **kwargs):         print('__init__ is called in B')         super(B, self).__init__(*args, **kwargs)  class AB(B,A):     def __init__(self, *args, **kwargs):         print('__init__ is called in AB')         super(AB, self).__init__(*args, **kwargs)  class C(A):     def __init__(self, *args, **kwargs):         print('__init__ is called in C')         super(C, self).__init__(*args, **kwargs)  class D(B):     def __init__(self, *args, **kwargs):         print('__init__ is called in D')         super(D, self).__init__(*args, **kwargs)   class CD(D,C):     def __init__(self, *args, **kwargs):         print('__init__ is called in CD')         super(CD, self).__init__(*args, **kwargs)  class ABCD(CD,AB):     def __init__(self, *args, **kwargs):         print('__init__ is called in ABCD')         super(ABCD, self).__init__(*args, **kwargs)     >>> abcd = ABCD() __init__ is called in ABCD __init__ is called in CD __init__ is called in D __init__ is called in AB __init__ is called in B __init__ is called in C __init__ is called in A 

So I think it's a good idea to think about using delegation instead of inheritance here.

class AB(object):     def __init__(self, x, y, z=0):         self.a = A(x,y)         self.b = B(z) 

So, you just create a and b instances of A and B classes inside AB object. And then may use them as you need through methods by referring to self.a and self.b.

To use or not delegation depends on your case which is not clear from your question. But it may be an option to consider.

like image 134
ovgolovin Avatar answered Oct 21 '22 09:10

ovgolovin


You didn't pass self.

class AB(A, B):     def __init__(self, x, y, z=0):         A.__init__(self, x, y)         B.__init__(self, z) 

Note that if this inheritance hierarchy gets more complicated, you'll run into problems with constructors not executing or getting reexecuted. Look into super (and the problems with super), and don't forget to inherit from object if you're on 2.x and your class doesn't inherit from anything else.

like image 33
user2357112 supports Monica Avatar answered Oct 21 '22 10:10

user2357112 supports Monica