j-i-l
j-i-l

Reputation: 10957

How would one decorate an inherited method in the child class?

I'm not quite sure how to use a decorator on an inherited method. Normally decorators are put before the definition but for an inherited function the definition is given in the parent and not the child class. I would like to avoid rewriting the definition of the method in the child class and simply specify to put the decorator around the inherited method.

To make myself clearer, here is a working example of what I mean:

class Person():
    def __init__(self, age):
        self.age = age

    @classmethod
    def gets_drink(cls, ):
        print "it's time to get a drink!"
        return "A beer"

def dec(some_f):
    def legal_issue(obj,):
        some_f(obj)
        return 'A Coke'
    return legal_issue

class Child(Person):
    def __init__(self, age):
        Person.__init__(self, age=age)

    #in principle i'd like to write
    #@dec
    #self.gets_drink
    #which does not work
    #the only working way I found is by defining the method as a classmethod
    #and write this:

    @dec
    def gets_drink(self, ):
        return Person.gets_drink()

dad = Person(42)
son = Child(12)

print dad.gets_drink()
print son.gets_drink()

The way it is done in this example works, but it looks somehow weird to me, replacing the method with a call to the original method only to be able to put the decorator. Also, for this to work, the method needs to be defined as a classmethod in the parent class.

Am I missing something? How would experienced developers do this?

Any help, insights, critical comments and free coffees are much appreciated!

Upvotes: 19

Views: 12526

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1121834

You don't have to use a decorator in decorator syntax. It is just a callable:

class Child(Person):
    def __init__(self, age):
        Person.__init__(self, age=age)

    gets_drink = dec(Person.gets_drink.__func__)

This adds the return value of the decorator to the child class as a method with the same name. The __func__ attribute unwraps the bound (class) method, retrieving the original function.

Note that your new method is now a regular method, not a classmethod, you'd have to re-wrap the result in a classmethod() call for that.

This works because the syntax

@decorator_expression
def function_definition():
    pass

is just syntactic sugar for:

def function_definition():
    pass
function_definition = decorator_expression(function_definition)

Upvotes: 21

Related Questions