Search code examples
pythonflaskdecoratorweb-frameworks

Is it possible to dynamically set the name of a decorator in python?


So, I was studying the FLASK framework in python and made my very first simple program and by default I've seen people using app = Flask(__name__) and just below that line they use the decorator @app.route("/"),So I thought that what would happen if I change the name of the variable to something else? Like in the code below I've changed it to something = Flask(__name__) so now I'm confused that how does it still work when I decorate the function index() with @something.route("/") ,is the name of the decorator function defined in FLASK changing dynamically? and if so how can I make my own decorators like this so that they too change their names dynamically?

from flask import Flask 

something = Flask(__name__)

@something.route("/")
def index():
    return "Hello, World!"


Solution

  • Decorator is just a syntactic sugar:

    def decorator(func):
        pass
    
    @decorator
    def decorated():
        pass
    

    is the same as:

    def decorator(func):
        pass
    
    def decorated():
        pass
    
    decorated = decorator(decorated)
    

    The decorator name is nothing more than a function that accepts one argument. You could even use print function as a decorator. Any valid callable will do:

    @print
    def index():
        pass
    

    Obviously that makes little sense, because:

    def index():
        pass
    
    index = print(index)
    

    Anyway that's how this could be implemented in flask

    class Flask:
        def route(self, url):
            def wrapper(func):
                # register route for url
                return func
    
            return wrapper
    
    something = Flask()
    
    @something.route("/")
    def index():
        pass
    

    something.route("/") is a function call that returns the real decorator which is actually the inner function named wrapper.

    So you can even do something like this:

    something_route_index = something.route("/")
    
    @something_route_index
    def index():
        pass