Reputation: 73
I have a decorator
def deco(func):
def inner(params):
#< DO STUFF WITH func >
return inner
And a base class
class GenericClass:
def __init__(self,value):
self.value = value
def method(self,params):
print 'NOT IMPLEMENTED YET'
def other_method(self):
print 'GOOD TO GO'
I would like to be able to decorate the "method" method on classes which are child of GenericClass, for exemple to check input/output or handle exceptions (the method "method" will be overrided)
what I want to do is something like...
class ChildClass(GenericClass):
@deco
def method(self,params):
#< NEW METHOD >
I am not an expert python developper and all the doc at that level is quite confusing (i.e. metaclasses, subtleties in decorators, __call__
method etc etc) and I didn't found the solution on SO.
Upvotes: 7
Views: 6100
Reputation: 79
pass a argument ignore_first
def test(ignore_first=False):
def decorator(func):
if not ignore_first:
def _func(x):
x*=10
return func(x)
else:
def _func(_,x):
x*=10
return func(_,x)
return _func
return decorator
test passed:
def decorator_test():
class A():
@test(ignore_first=True)
def f(self, x):
print(x)
@classmethod
@test(ignore_first=True)
def class_f(cls, x):
print(x)
@staticmethod
@test()
def static_f(x):
print(x)
a=A()
@test()
def f(x):
print(x)
def f1(x):
@test()
def f1_f(x):
print(x)
f1_f(x)
a.f(11)
a.class_f(22)
a.static_f(33)
f(44)
f1(55)
Upvotes: 0
Reputation: 149
I figured it out. The trick is that module-level functions (except for closures, I guess, which you probably don't want to decorate anyway) have a simple name while methods at least have two parts in their qualified name. Forget about inspect.ismethod - for some reason it just won't work in this case, although it should be the obvious choice, possibly a bug.
def can(*fargs):
def wrapper(func):
if len(func.__qualname__.split('.')) > 1:
def calling(self, *args, **kwargs):
self, thing = args[0], args[1]
do_stuff(thing)
func(*args, **kwargs)
else:
def calling(*args, **kwargs):
thing = args[0]
do_stuff(thing)
func(*args, **kwargs)
return calling
return wrapper
class C:
@can(2, 3)
def test(self, x):
print(7, ismethod(self.test), x)
@can()
def test(x):
print(8, ismethod(test), x)
c = C()
c.test(12)
test(8)
Upvotes: 4
Reputation: 2040
Got it. You are basically asking how to write a decorator which can be applied to both functions and methods. It's possible:
def deco(func):
def inner(*args):
print('DECORATED: args={}'.format(args))
func(*args)
return inner
class Class:
@deco
def method(self, param): print('PARAM is {}'.format(param))
@deco
def func(a, b, c): print('{} {} {}'.format(a, b, c))
Class().method('X')
func(1, 2, 3)
OUTPUT:
DECORATED: args=(<__main__.Class instance at 0x7f740c6297a0>, 'X')
PARAM is X
DECORATED: args=(1, 2, 3)
1 2 3
P.S.
One year later I found one useful post (which was asked 8 years ago) here: Using the same decorator (with arguments) with functions and methods. The approach described there will be useful if you are care of actual parameters of the decorated function.
Upvotes: 6