1

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!

Community
  • 1
  • 1
Gulzar
  • 23,452
  • 27
  • 113
  • 201

2 Answers2

2

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)

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • In the first link in the question, please look at the `Make it static (don’t)` part. Why is that different here? – Gulzar May 15 '19 at 07:53
  • @Gulzar That is an interesting question... I tested my code (with `@staticmethod`) in both Python 2.7 and 3.6 and it worked fine on both. But please note that in the linked post, the decorators are declared without the `cls` implicit argument despite being classmethods which I personally dislike. – Serge Ballesta May 15 '19 at 08:05
  • @Gulzar The catch described in the `Make it static (don't)` paragraph only applies if the decorator is implemented as a static method _inside_ the class that is using it to decorate methods. Your example alerady employs the solution outlined in the `Declare in another class` paragraph, hence making the decorator method static is perfectly fine. – shmee May 15 '19 at 08:13
0

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")
Gulzar
  • 23,452
  • 27
  • 113
  • 201
J Lossner
  • 129
  • 10