Suppose I have a simple decorator, and a classes method and a function which I both decorate with that decorator:
import functools
def decorator(func):
@functools.wraps(func)
def call(*args):
print(args)
func(*args)
return call
class cls:
@decorator
def meth(self, a):
pass
@decorator
def func(c):
pass
cls().meth("a")
func("c")
I get following output:
(<__main__.cls object at 0x7f4665c50130>, 'a')
('c',)
But I want to remove the self
argument when the decorator is used on a method, so that the output becomes:
('a',)
('c',)
But, if I simply add args.pop(0)
, I will remove the first argument even if it is not self
. How can I only remove the first argument if it is self
?
Note: I read some solutions with a long code using inspect
- but there must be a shorter, easier way in this great and simple-to-use programming language ?
EDIT: Using @staticmethod
is not an option for me, because I need the self
parameter in the method itself. I only don't want it to get printed.
The simplest way to achieve this is to explicitly tell your decorator that it's decorating a class method. Your decorator will take a keyword argument denoting that.
def decorator(is_classmethod=False):
def wrapper(func):
@functools.wraps(func)
def call(*args):
if is_classmethod:
print(args[1:]) # omit args[0]
else:
print(args)
return func(*args) # pass all the args to the function
return call
return wrapper
class cls:
@decorator(is_classmethod=True)
def meth(self, a):
pass
@decorator
def func(c):
pass
cls().meth("a")
func("c")
Since the user already knows what they're decorating, you can avoid complicated introspection code in the decorator, and give the user flexibility of processing the first argument or not.
please take a look at my answer to this SO question: Decorators with parameters? for a cleaner (IMHO) way to write decorators with parameters. There are some really good answers in that post.