Reputation: 795
I succeeded in creating a decorator that decorates any types of classes, adding a standard interface to them all, for easy access, integration, etc...
I have resisted using metaclasses, as literature on this point says that it is an overkill and most times can be replaced by say class decorators. What troubles me is the following:
def Decorator(somearg):
def wrapper(cls):
clsinit = cls.__init__
cls.members = []
def __init__(self, *args, **kwargs):
#do something with somearg...
self.__class__.members.append(self)
clsinit(self,*args,**kwargs)
cls.__init__ = clsinit
return cls
return wrapper
@Decorator('thearg')
class A(object):
pass
a = A()
b = A()
Using python debugger, at import time, class A is immediately decorated with argument 'thearg'. But each time I instantiate A(), the instance calls straight to the init defined in the decorator, without passing through the previous layers. That's great because I want my class to record each members and not be reset every time a new instance is instantiated. But I am not sure I understand why.
Can someone explain the physics of the python interpreter in this specific case?
Upvotes: 1
Views: 1225
Reputation: 123501
OK, referring to the corrected code in your updated question, you ask:
"But each time I instantiate A(), the instance calls straight to the init defined in the decorator, without passing through the previous layers."
It does this because when thewrapper()
class decorator function, created by theDecorator()
function, is applied toclass A
, it replacesA
's__init__()
with its own dynamically defined wrapper.__init__()
function. That function is what is called whenever A
s are instantiated and it's written first do some things and then call the originalA.__init__()
at the end.
Upvotes: 0
Reputation: 795
Thanks @JBernardo and @martineau for your answers. My objective with this code was to implement container like behaviour of the class objects in order to reach their instances more easily. But I want to access instances relative to each class one at a time, not all in the same inherited class.
Actually working on the code again helped me understand my issue:
a)Decorator is called with its argument somearg
b) the content of the next line (definition of class A) is used as the argument to the wrapper function. Inside this function I: 1) remember the initial class init to avoid infinite recursion 2) define a new init to make changes at instance level 3) bind a new init method for the class 4) return a transformed class object
c) because the return is a class object, every time I instantiate an instance, I go straight to init because this is how it was bound in the new class.
Upvotes: 0
Reputation: 33407
If you're just changing the class and not creating a new one, there's no need for a wrapper function (that should have been a wrapper class):
def Decorator(cls):
clsinit = cls.__init__
cls.members = []
def __init__(self, *args, **kwargs):
clsinit(self, *args, **kwargs)
self.__class__.members.append(self)
cls.__init__ = __init__
return cls
BTW, you should just create a base class and inherit from it:
class Base(object):
members = []
def __init__(self):
self.__class__.members.append(self)
class A(Base):
pass
Much cleaner
Upvotes: 2