Reputation: 385
My class hierarchy is set up so that every child's __init__()
must set self._init_has_run()
to False
, call the parent's __init__()
, then do their own __init__()
, and finally set self._init_has_run()
to True
. I have the following code:
class Parent:
def __init__(self, arg1, arg2):
pass # do stuff
def init(cls, fun):
def decorated_init(self, *args, **kwargs):
self._init_has_run = False
x = super()
super().__init__(*args, **kwargs)
fun(self, *args, **kwargs)
self._init_has_run = True
return decorated_init
class Child(Parent):
@Parent.init
def __init__(self, arg1, arg2):
pass # do stuff
Since there are a number of subclasses that follow the same general pattern for __init__()
, and I can't figure out how to use metaclasses, I am using a decorator to consolidate the repetitive logic and then just applying that decorator to all descendant __init__()
methods.
Python is throwing the following:
File "filename.py", line 82, in decorated_init
super().__init__(*args, **kwargs)
TypeError: object.__init__() takes no parameters
I confirmed through the debugger that the toggling of self._init_has_run
works fine and super()
is resolving to the Parent class, but when the decorator tries to call super().__init__(*args, **kwargs)
, why does Python try to call object.__init__()
instead?
Upvotes: 4
Views: 1892
Reputation: 3129
You can easily use metaclasses to do some pre/post-init stuff. Consider this example:
class Meta(type):
def __new__(meta, *args):
# This is something like 'class constructor'.
# It is called once for every new class definition.
# It sets default value of '_init_has_run' for all new objects.
# This is analog to `class Foo: _init_has_run = False`:
# new objects will all have _init_has_run set to False by default.
cls = super(Parent, meta).__new__(meta, *args)
cls._init_has_run = False
return cls
def __call__(cls, *args, **kwargs):
# This is called each time you create new object.
# It will run new object's constructor
# and change _init_has_run to False.
obj = type.__call__(cls, *args, **kwargs)
obj._init_has_run = True
return obj
class Child:
__metaclass__ = Meta
def __init__(self):
print 'init:', self._init_has_run
def foo(self):
print 'foo:', self._init_has_run
a = Child()
a.foo()
a = Child()
a.foo()
Output:
init: False
foo: True
init: False
foo: True
Hope this helps!
Upvotes: 3