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)
Related
I have the following code:
class Parent:
def __init__(self) -> None:
print(f"init Parent")
class Child(Parent):
def __init__(self) -> None:
super().__init__()
print(f"init Child")
def post_init(self):
print(f"post init")
Child()
Here I get the output:
init Parent
init Child
Is it possible to modify the code in the Parent-class to call the post_init method from the Child-class automatically after the ___init___() of the Parent and the Child-class. I would like to have the following output:
init Parent
init Child
post init
I don't want to modify the Child-class!!!
I have already tried to define a decorator in the Parent-class, but I failed.
EDIT:
The Parent-class is a pattern-class and is multiple used in multiple children. So I don't want to take care every time i use the Child-class. Is it possible on using the abstract-package?
Using __init_subclass__ to override the __init__:
class Parent:
def __init__(self):
print(f"init Parent")
def __init_subclass__(cls, *args, **kwargs):
super().__init_subclass__(*args, **kwargs)
def new_init(self, *args, init=cls.__init__, **kwargs):
init(self, *args, **kwargs)
self.post_init()
cls.__init__ = new_init
Supporting grandchildren
To only call after a grandchild class's __init__, we need an additional check to see if the __init__ being called is that of the grandchild's:
class Parent:
def __init__(self):
print("init Parent")
def __init_subclass__(cls, *args, **kwargs):
super().__init_subclass__(*args, **kwargs)
def new_init(self, *args, init=cls.__init__, **kwargs):
init(self, *args, **kwargs)
if cls is type(self):
self.post_init()
cls.__init__ = new_init
Example usage:
class Child(Parent):
def __init__(self):
super().__init__()
print("init Child")
def post_init(self):
print("post init")
class Grandchild(Child):
def __init__(self):
super().__init__()
print("init Grandchild")
_ = Grandchild()
Output:
init Parent
init Child
init Grandchild
post init
You can also write code like this:
class Meta(type):
def __call__(cls, *args, **kwargs):
instance = super().__call__(*args, **kwargs)
instance.post_init()
return instance
class Parent(metaclass=Meta):
def __init__(self) -> None:
print(f"init Parent")
class Child(Parent):
def __init__(self) -> None:
super().__init__()
print(f"init Child")
def post_init(self):
print(f"post init")
Child()
I have a class hierarchy - which may be multiple inheritance, for example:
class Base:
def __init__(self):
print("Base")
class A(Base):
def __init__(self):
super().__init__()
print("A")
class B(Base):
def __init__(self):
super().__init__()
print("B")
class C(A, B):
def __init__(self):
super().__init__()
print("C")
x = C()
will print
Base
B
A
C
as expected.
what I'd like to do is somehow print Done after this is all finished, by defining a method or similar in Base that will be called when C.__init__() has completed.
What I can't do:
Put code in Base.__init__ as that gets called too early.
Put code in C.__init__ as I want this to apply to all potential subclasses
I've tried various things with metaclasses and haven't managed to make anything work yet - any clever ideas?
The call to __init__ is handled by the call to type.__call__, once __new__ has returned. You could define a metaclass that overrides __call__ to handle your post-init code; I believe this is the only way to avoid having the code be called prematurely whenever you subclass, since __call__ won't be invoked for or by any of the ancestor classes.
class PostInitHook(type):
def __call__(cls, *args, **kwargs):
rv = super().__call__(*args, **kwargs)
print("Done")
return rv
class Base(metaclass=PostInitHook):
def __init__(self):
print("Base")
...
More generally, you could replace print("Done") a class specific hook, for example,
class PostInitHook(type):
def __new__(metacls, *args, **kwargs):
cls = super().__new__(metacls, *args, **kwargs)
try:
cls._post_init
except AttributeError:
raise TypeError("Failed to define _post_init")
return cls
def __call__(cls, *args, **kwargs):
rv = super().__call__(*args, **kwargs)
rv._post_init()
return rv
class Base(metaclass=PostInitHook):
def __init__(self):
print("Base")
def _post_init(self):
print("Done")
Then
>>> a = A()
Base
A
Done
>>> b = B()
Base
B
Done
>>> c = C()
Base
B
A
C
Done
>>> class D(C):
... def __init__(self):
... super().__init__()
... print("D")
...
>>> d = D()
Base
B
A
C
D
Done
>>>
I am trying to create a decorator that will inject some functionality to wrapped class __init__ method. This is what works.
class Decorator:
def __init__(self, arg):
print(arg)
self.arg = arg
def __call__(self, cls):
print(cls)
class Wrapped(cls):
def __init__(self, first_arg, second_arg, **kwargs):
cls.__init__(self, first_arg, second_arg, **kwargs)
print('in wrapped init', self.variable)
return Wrapped
#Decorator('random_string')
class TestClass:
def __init__(self, first_arg, second_arg, **kwargs):
self.variable = 10
print('TestClass init')
test = TestClass(first_arg='one', second_arg='two')
and produces
random_string
<class '__main__.TestClass'>
TestClass init
in wrapped init 10
for some mysterious reasons code is no longer working after removing decorator param (random string in this case)
#Decorator
class TestClass:
def __init__(self, first_arg, second_arg, **kwargs):
self.variable = 10
print('TestClass init')
Output:
Traceback (most recent call last):
File "/home/python_examples/test_decorators.py", line 24, in <module>
test = TestClass(first_arg='one', second_arg='two')
<class '__main__.TestClass'>
TypeError: __call__() got an unexpected keyword argument 'first_arg'
Two questions:
is this a well-known and valid approach to decorate classes?
why is the never used 'random_string' param crucial?
It's because your decorator class takes arg as a constructor argument. So when you remove your decorator param, make sure that you have removed that parameter from the __init__ method too. Changing this
class Decorator:
def __init__(self, arg):
print(arg)
self.arg = arg
def __call__(self, cls):
print(cls)
class Wrapped(cls):
def __init__(self, first_arg, second_arg, **kwargs):
cls.__init__(self, first_arg, second_arg, **kwargs)
print('in wrapped init', self.variable)
return Wrapped
to this
class Decorator:
def __init__(self):
pass
def __call__(self, cls):
print(cls)
class Wrapped(cls):
def __init__(self, first_arg, second_arg, **kwargs):
cls.__init__(self, first_arg, second_arg, **kwargs)
print('in wrapped init', self.variable)
return Wrapped
will solve your problem.
Actually, this is how it works:
# Assume you have a decorator class and a class named A which is needed to decorate.
#Decorator
class A:
...
# is the same as
class A:
...
A = Decorator()(A)
That's why you need to define __cal__.
And here you can see that if your Decorator accepts some parameters to initialize, you need to use something like A = Decorator(xxx)(A). And the equivalent in decorator syntax is:
#Decorator(xxx)
class A:
...
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
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.