This question already has answers here:
Python: Correct way to initialize when superclasses accept different arguments?
(2 answers)
Closed 9 years ago.
What is the best way to deal with an inheritance structure like this:
class A(object):
def __init__(self):
print('A')
class B(A):
def __init__(self, foo):
super(B, self).__init__()
self.foo = foo
print('B')
class C(A):
def __init__(self, bar):
super(C, self).__init__()
self.bar = bar
print('C')
class D(B, C):
def __init__(self, foo, bar):
super(D, self).__init__(foo, bar)
Essentially, I want to be able to call:
>>> d = D('bar', 'ram ewe')
>>> d.foo
'bar'
>>> d.bar
'ram ewe'
Currently, the super(D, self).__init__(foo, bar) raises TypeError: __init__() takes exactly 2 arguments (3 given)
EDIT
Working answer, thanks to Daniel Roseman.
class A(object):
def __init__(self, *args, **kwargs):
print('A')
class B(A):
def __init__(self, foo, *args, **kwargs):
super(B, self).__init__(*args, **kwargs)
self.foo = foo
print('B')
class C(A):
def __init__(self, bar, *args, **kwargs):
super(C, self).__init__(*args, **kwargs)
self.bar = bar
print('C')
class D(B, C):
def __init__(self, foo, bar, *args, **kwargs):
super(D, self).__init__(foo, bar, *args, **kwargs)
The best way is to always ensure the methods are both defined and called using the *args, **kwargs syntax. That means they will get the parameters they need and ignore the rest.
Related
For example I have something like this:
class A(object):
def __init__(self):
pass
def foo(self, a, b, c):
return a + b + c
class B(object):
def __init__(self):
self.b = A()
def wrapper_func(func):
def wrapper(self, *args, **kwargs):
return func(self, a=3, *args, **kwargs)
return wrapper
class C(B):
def __init__(self):
pass
#wrapper_func
def ???
Is it possible to some how overload and then wrap method foo of the field of parent B class in python without inherits from class A? I need the wrapper indeed because I have the different methods with same arguments, but in the same time I have to save original class B methods native (besides overloading).
Initialize C's parent class using super and then pass all the parameters to the foo method of the composed class instance A() via the inherited attribute b of the class C:
def wrapper_func(func):
def wrapper(self, *args, **kwargs):
kwargs['a'] = 3
return func(self, *args, **kwargs)
return wrapper
class C(B):
def __init__(self):
super(C, self).__init__()
#wrapper_func
def bar(self, *args, **kwargs):
return self.b.foo(*args, **kwargs) # access foo via attribute b
Trial:
c = C()
print(c.bar(a=1, b=2, c=3))
# 8 -> 3+2+3
To make the call to the decorated function via c.b.foo, patch the c.b.foo method with the new bar method:
class C(B):
def __init__(self):
super(C, self).__init__()
self._b_foo = self.b.foo
self.b.foo = self.bar
#wrapper_func
def bar(self, *args, **kwargs):
return self._b_foo(*args, **kwargs)
Trial:
c = C()
print(c.b.foo(a=1, b=2, c=3))
# 8 -> 3+2+3
I have two classes:
class A(object):
"""Instance of this class must by only one"""
def some_function(self):
print "A function"
[... other functions ...]
class B(object):
"""Many instances of this class"""
[... functions ...]
And I want to init only one object of class A from class B, I write this:
class B(object):
a_instance = A()
b_instances = []
def __init__(self, *args, **kwargs):
B.a_instance.some_function()
B.b_instances.append(self)
super(B, self).__init__(*args, **kwargs)
def update(self):
print "Update instance"
#classmethod
def super_function(self):
print "My super B function"
for instance in B.b_instances:
instance.update()
Is this correct?
And second question: How to call "super_function" from instance of class A?
Let's say I have the following parent and child classes:
class A(object):
def __init__(self, *args, **kwargs):
self.a = kwargs.get('a', 'default_A')
self.b = kwargs.get('b', 'default_B')
class B(A):
a = "override_A"
def __init__(self, *args, **kwargs):
super(B, self).__init__(**kwargs)
b = B()
print b.b # this is "default_B", as expected
print b.a # I expected this to be "override_A"
What am I doing wrong here? I've tried to understand how inheritance works via answers like this one but haven't found something that describes this specific requirement.
You're mixing class and instance variables. B.a is a class variable, which is shadowed by the instance variable set in A.__init__().
You could for example use dict.setdefault():
class B(A):
def __init__(self, *args, **kwargs):
# If the key 'a' exists, this'll be effectively no-operation.
# If not, then 'a' is set to 'override_A'.
kwargs.setdefault('a', 'override_A')
super(B, self).__init__(**kwargs)
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.
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.
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.
I have a class:
class A(object):
def __init__(self, *args):
# impl
Also a "mixin", basically another class with some data and methods:
class Mixin(object):
def __init__(self):
self.data = []
def a_method(self):
# do something
Now I create a subclass of A with the mixin:
class AWithMixin(A, Mixin):
pass
My problem is that I want the constructors of A and Mixin both called. I considered giving AWithMixin a constructor of its own, in which the super was called, but the constructors of the super classes have different argument lists. What is the best resolution?
class A_1(object):
def __init__(self, *args, **kwargs):
print 'A_1 constructor'
super(A_1, self).__init__(*args, **kwargs)
class A_2(object):
def __init__(self, *args, **kwargs):
print 'A_2 constructor'
super(A_2, self).__init__(*args, **kwargs)
class B(A_1, A_2):
def __init__(self, *args, **kwargs):
super(B, self).__init__(*args, **kwargs)
print 'B constructor'
def main():
b = B()
return 0
if __name__ == '__main__':
main()
A_1 constructor
A_2 constructor
B constructor
I'm fairly new to OOP too, but what is the problem on this code:
class AWithMixin(A, Mixin):
def __init__(self, *args):
A.__init__(self, *args)
Mixin.__init__(self)