I’m using a custom metaclass, to decorate methods of a class. However, it currently only decorates methods defined directly in the class and not those inherited from the parent class.
My classes are structured as follows:
class Abstract1(ABC):
class A(Abstract1):
def fun1(self):
def fun2(self):
class DecoMeta(ABCMeta):
class B(A, metaclass=DecoMeta):
def hello(self):
My DecoMeta class looks like following:
class DecoMeta(ABCMeta):
""" Decorator class
"""
def __new__(mcs, name: str, bases: tuple, attrs: dict):
for attr_name, attr_value, in attrs.items():
if isinstance(attr_value, types.FunctionType):
attrs[attr_name] = mcs.deco(attr_value)
return super(DecoMeta, mcs).__new__(mcs, name, bases, attrs)
@classmethod
def deco(mcs, func):
""" Decorate func
"""
async def wrapper(*args, **kwargs):
result = await func(*args, **kwargs)
# print(args[0].__dict__.keys())
args[0].some_function(). # i.e. self.some_function()
return result
return wrapper
When I print args[0].dict.keys() it only prints out the functions defined in class B. So that means, decoMeta only decorates those functions(in this case hello()). I want to decorate all functions defined in the parent class i.e. class A(like fun1() and fun2()) using this DecoMeta. Is there a way to do it?
You have to register A
to the metaclass as well.
One way you can achieve that is by passing the metaclass to the parent class as well:
class Abstract1(ABC):
pass
class DecoMeta(ABCMeta):
pass
class A(Abstract1, metaclass=DecoMeta):
def fun1(self):
pass
def fun2(self):
pass
class B(A, metaclass=DecoMeta):
def hello(self):
pass
With the modified DecoMeta
, just to display the attr_name
:
class DecoMeta(ABCMeta):
""" Decorator class
"""
def __new__(mcs, name: str, bases: tuple, attrs: dict):
for attr_name, attr_value, in attrs.items():
if isinstance(attr_value, types.FunctionType):
print(attr_name)
# attrs[attr_name] = mcs.deco(attr_value)
return super(DecoMeta, mcs).__new__(mcs, name, bases, attrs)
B()
outputs
fun1
fun2
hello
Otherwise, if you don't want to edit A
at all, you can do something fancier like that:
class DecoMeta(ABCMeta):
""" Decorator class
"""
def __new__(mcs, name: str, bases: tuple, attrs: dict):
all_attrs = attrs | {
k: v for k, v in base.__dict__.items()
for base in bases
}
for attr_name, attr_value, in all_attrs.items():
if isinstance(attr_value, types.FunctionType):
print(attr_name)
# attrs[attr_name] = mcs.deco(attr_value)
return super(DecoMeta, mcs).__new__(mcs, name, bases, attrs)
Which gives the same output.