Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't Python decorators be chained across definitions?

Why arn't the following two scripts equivalent?

(Taken from another question: Understanding Python Decorators)

def makebold(fn):
    def wrapped():
        return "<b>" + fn() + "</b>"
    return wrapped

def makeitalic(fn):
    def wrapped():
        return "<i>" + fn() + "</i>"
    return wrapped

@makebold
@makeitalic
def hello():
    return "hello world"

print hello() ## returns <b><i>hello world</i></b>

and with a decorated decorator:

def makebold(fn):
    def wrapped():
        return "<b>" + fn() + "</b>"
    return wrapped

@makebold
def makeitalic(fn):
    def wrapped():
        return "<i>" + fn() + "</i>"
    return wrapped

@makeitalic
def hello():
    return "hello world"

print hello() ## TypeError: wrapped() takes no arguments (1 given)

Why do I want to know? I've written a retry decorator to catch MySQLdb exceptions - if the exception is transient (e.g. Timeout) it will re-call the function after sleeping a bit.

I've also got a modifies_db decorator which takes care of some cache-related housekeeping. modifies_db is decorated with retry, so I assumed that all functions decorated with modifies_db would also retry implicitly. Where did I go wrong?

like image 633
RobM Avatar asked Jan 19 '10 14:01

RobM


People also ask

Can decorators be chained in Python?

So, here in this post, we are going to learn about Decorator Chaining. Chaining decorators means applying more than one decorator inside a function. Python allows us to implement more than one decorator to a function. It makes decorators useful for reusable building blocks as it accumulates several effects together.

What is true about decorators in Python?

Decorators are a very powerful and useful tool in Python since it allows programmers to modify the behaviour of a function or class. Decorators allow us to wrap another function in order to extend the behaviour of the wrapped function, without permanently modifying it.

Why you should wrap decorators in Python?

Python decorators are a powerful concept that allow you to "wrap" a function with another function. The idea of a decorator is to abstract away something that you want a function or class to do, besides its normal responsibility. This can be helpful for many reasons such as code reuse, and sticking to curlys law.

Can decorators be async?

Not using async also gives the advantage of being able to use the same decorator in normal functions as well. The only advantage of having async with a single return await is to make it clearly documented the function must be awaited, but that doesn't matter in a decorator wrapper.


1 Answers

The problem with the second example is that

@makebold
def makeitalic(fn):
    def wrapped():
        return "<i>" + fn() + "</i>"
    return wrapped

is trying to decorate makeitalic, the decorator, and not wrapped, the function it returns.

You can do what I think you intend with something like this:

def makeitalic(fn):
    @makebold
    def wrapped():
        return "<i>" + fn() + "</i>"
    return wrapped

Here makeitalic uses makebold to decorate wrapped.

like image 178
Will Harris Avatar answered Sep 25 '22 07:09

Will Harris