Skip to content
Advertisement

decorating decorators: try to get my head around understanding it

I’m trying to understand how to decorate decorators, and wanted to try out the following:

Let’s say I have two decorators and apply them to the function hello():

def wrap(f):
    def wrapper():
        return " ".join(f())
    return wrapper


def upper(f):
    def uppercase(*args, **kargs):
        a,b = f(*args, **kargs)
        return a.upper(), b.upper()
    return uppercase

@wrap
@upper
def hello():
    return "hello","world"

print(hello())

Then I have to start adding other decorators for other functions, but in general the @wrap decorator will “wrap all of them”

def lower(f):
    def lowercase(*args, **kargs):
        a,b = f(*args, **kargs)
        return a.lower(), b.lower()
    return lowercase

@wrap
@lower
def byebye():
    return "bye", "bye"

How do I write a decorator, which decorates my @lower and @upper decorators? See below:

@wrap
def lower():
    ...

@wrap
def upper():
    ...

To achieve the same result as above by only doing:

@upper
def hello():
    ...

@lower
def byebye():
    ...

Advertisement

Answer

Here’s a generic (and slightly convoluted) solution for decorating decorators with decorators (Yay!).

# A second-order decorator
def decdec(inner_dec):
    def ddmain(outer_dec):
        def decwrapper(f):
            wrapped = inner_dec(outer_dec(f))
            def fwrapper(*args, **kwargs):
               return wrapped(*args, **kwargs)
            return fwrapper
        return decwrapper
    return ddmain

def wrap(f):
    def wrapper():
        return " ".join(f())
    return wrapper


# Decorate upper (a decorator) with wrap (another decorator)
@decdec(wrap)
def upper(f):
    def uppercase(*args, **kargs):
        a,b = f(*args, **kargs)
        return a.upper(), b.upper()
    return uppercase

@upper
def hello():
    return "hello","world"

print(hello())
Advertisement