Reputation: 903
I'm trying to wrap all of the methods inside a class I wrote with a specific wrapper method.
My class inherits from the python dict class, and I want to wrap all of the methods of this parent class, such as __setitem__, __getitem__, etc.
In my attempts to achieve that I have written a meta class that wraps all the methods inside it's child class, using the __init__ method in the meta class, so I can access the child class's object (and not it's class definition which does not include the parent methods).
However, after running the code, I see the wrapper method is never called. Meaning the wrapping didn't succeed.
Can you help with figuring out what went wrong?
My code:
def wrapper(func):
def wrapped(self, *args, **kwargs):
print 'wrapper.__call__()'
res = func(self, *args, **kwargs)
return res
return wrapped
class MyMeta(type):
def __init__(cls, classname, bases, class_dict):
print 'meta.__init__()'
new_class_dict = {}
for attr_name in dir(cls):
attr = getattr(cls, attr_name)
if hasattr(attr, '__call__'):
attr = wrapper(attr)
new_class_dict[attr_name] = attr
return super(MyMeta, cls).__init__(classname, bases, new_class_dict)
class MyDict(dict):
__metaclass__ = MyMeta
def __init__(self, *args):
print 'child.__init__()'
super(MyDict, self).__init__(*args)
d = MyDict({'key': 'value'})
d['new_key'] = 'new_value'
The printout I get is:
meta.__init__()
child.__init__()
without any reference to the wrapper.__call__() print I placed inside the wrapped method...
Upvotes: 2
Views: 1270
Reputation: 77902
When the metaclass __init__
gets called, the class
object has already been built so modifying the attributes dict (class_dict
in your code) at this stage is totally useless indeed. You want to use setattr
instead:
class MyMeta(type):
def __init__(cls, classname, bases, class_dict):
for attr_name in dir(cls):
if attr_name == "__class__":
# the metaclass is a callable attribute too,
# but we want to leave this one alone
continue
attr = getattr(cls, attr_name)
if hasattr(attr, '__call__'):
attr = wrapper(attr)
setattr(cls, attr_name, attr)
# not need for the `return` here
super(MyMeta, cls).__init__(classname, bases, class_dict)
Upvotes: 3