Since Python 3.9, decorators are a lot more flexible than before. Any callable can now be a decorator. With that being said, a couple of (seen from the perspective of a Python 3.8 developer) crazy things can be done.
Suppose we have three decorators transforming the returned text of a given function:
# voices.py
import functools
def normal(func):
return func
def shout(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs).upper()
return wrapper
def whisper(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs).lower()
return wrapper
From Python 3.9 onwards, we can put all three into a dictionary, let the user decide, which one to use, and apply it to a given function as follows:
# previous code in voices.py
DECORATORS = {
"normal": normal,
"shout": shout,
"whisper": whisper,
}
voice = input(f"Choose your voice ({', '.join(DECORATORS)}):")
@DECORATORS[voice]
def get_story():
return "This is a sample text."
print(get_story())
Executing the script at hand gives us the following output:
$ python voices.py
Choose your voice (normal, shout, whisper): shout
THIS IS A SAMPLE TEXT.
Groups: language reference