'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 objectsalute
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 |