Search code examples
pythonpython-3.xobjectdecoratorself

How to remove the self argument from a decorator's arguments if there is one


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.


Solution

  • 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.