'Python: Do we really need wrappers for decorators? (Revisited)

A similar question has been asked, but the OP compares the wrapper to a function without return, thus the answers focus on that "fault".
I understood decorators and why we use them instead of sub-classes after reading this article. There, they write the following example in which they introduce the need of the wrapper:

def uppercase_decorator(function):
    def wrapper():
        funct = function()
        make_uppercase = funct.upper()
        return make_uppercase

    return wrapper

Yet, I can write "the same thing" (I'm expecting you to say it is not), this way:

def uppercase_decorator(function): #previously 'function' was called 'message' but 'function' is clearer for comparison. 
    make_uppercase = function().upper
    return make_uppercase

Both versions could be applied to this fn, resulting in the same output ('HI ALL!') when calling salute():

@uppercase_decorator
def salute():
    return 'Hi all!'

If main fn returns a random string (thanks to @wim for suggestion) one can notice that every time it runs it, the one without wrapper always returns the same thing when running the sayGarbage() line:

def decorateIt(fn):
    toUpper = fn().upper  
    return toUpper  

def decorateW(fn):  
    def wrapper():  
        funct = fn()  
        toUpper = funct.upper()  
        return toUpper  
    return wrapper  

import random, string  

@decorateIt  
def sayGarbage():  
    return "".join(random.choice(string.ascii_lowercase) for i in range(6))  

sayGarbage()

Why is that so?



Solution 1:[1]

Let's look what happens here:

def uppercase_decorator(message): 
    make_uppercase = func().upper
    return make_uppercase

@uppercase_decorator
def salute():
    return 'Hi all!'
  • Firstly, uppercase_decorator gets defined.
  • Secondly, salute gets defined.
  • Thirdly, uppercase_decorator gets called, passing in the function object salute in the place of the first positional argument (message).

At this point, the code will crash, because func() gets called and that was not defined anywhere. If it worked in your Python REPL session, then you must have had the name func lying around in global scope from earlier, and so the code only appeared to work by coincidence.

Do we really need wrappers for decorators?

No. It is possible to write decorators in a nicer "closureless" style like you suggest. To write a decorator without the nested function, you can use the popular library decorator.py. It works like this:

from decorator import decorator

@decorator
def uppercase_decorator(func, *args, **kwargs):
    result = func(*args, **kwargs)
    return result.upper()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1