Gulzar
Gulzar

Reputation: 27966

How to declare decorator inside a class, to decorate an already decorated, inherited method?

I want to create a decorator which is a class member, and that is going to decorate an inherited method, which is decorated.

example code:

class A(object):
    __metaclass__ = ABCMeta
    def __init__(self):
        pass

    @classmethod
    def the_decorator(cls, decorated):  # <-----this is what i want, with or without self/cls as an argument
        def decorator()
            #do stuff before
            decorated()
            print "decorator was called!"
            #do stuff after
        return decorator

    @abstractmethod
    def inherited():
        raise NotImplemented


class B(A):
    def __init__(self):
        super(B,self).__init__()

    #@A.the_decorator <--- this is what I want, 
    @overrides
    #@A.the_decorator <--- or this
    def inherited():
        print "B.inherited was invoked"

and

b = B()
b.inherited()

should output

B.inherited was invoked

decorator was called!


Having read this guide on decorators as class members, I still haven't been able to figure out how to decorate inherited methods with decorators defined in the super class.


Note, here @overrides is defined by the overrides package pip install overrides


Also note i am currently using python 2.7, but would love both 2.7 and 3+ answers.

Thanks!

Upvotes: 1

Views: 73

Answers (2)

J Lossner
J Lossner

Reputation: 139

Using Python 3, your code is just missing a few self arguments to be able to call the function with b.inherited()

class A(object):
    __metaclass__ = ABCMeta
    def __init__(self):
        pass

    @classmethod
    def the_decorator(cls, decorated): 
        def decorator(*args, **kwargs):
            #do stuff before
            decorated(*args, **kwargs)
            print("decorator was called!")
            #do stuff after
        return decorator

    @abstractmethod
    def inherited(self):
        raise NotImplemented


class B(A):
    def __init__(self):
        super(B,self).__init__()

    @A.the_decorator
    @overrides
    def inherited(self):
        print("B.inherited was invoked")

Upvotes: 0

Serge Ballesta
Serge Ballesta

Reputation: 148965

You were not that far!

The key is that a decorator will receive one single argument which is the decorated function so it can only be a statmethod. And you also forgot that normal methods should be declared with a self argument.

But this code should work:

class A(object):
    __metaclass__ = ABCMeta
    def __init__(self):
        pass

    @staticmethod
    def the_decorator(decorated):  # <-----this is what i want, with or without self/cls as an argument
        def decorator(self):
            #do stuff before
            decorated(self)
            print "decorator was called!"
            #do stuff after
        return decorator

    @abstractmethod
    def inherited():
        raise NotImplemented


class B(A):
    def __init__(self):
        super(B,self).__init__()

    @A.the_decorator #<--- this is what I want, 
    @overrides
    #@A.the_decorator <--- or this
    def inherited(self):
        print "B.inherited was invoked"

I could test it under Python 2.7 except for the @overrides decorator (I commented it in my tests)

Upvotes: 2

Related Questions