2

I was going throught the basic Flask tutorial which has the following code:

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run()

Also I went through the basics of Python decorators from many websites including Stackoverflow Decorators

I was of the assumption that in the previous code, the function hello will be changed and decorated, and for running the application I need to call the function hello() somewhere. How does Flask determine the name of the function it has to call.

Does just wrapping the function definition with a decorator somehow marks that function? If so, how?

For example in the below code, somewhere down the line I'm calling the function I've decorated:

def decorate(foo):
    print("I'm doing a lot of important stuff right now")

    def inner():
        print("Some stuff 1")
        foo()
        print("Some stuff 2")

    return inner

@decorate
def hello():
    print("Hello")

hello()
Community
  • 1
  • 1
Kartik Anand
  • 4,513
  • 5
  • 41
  • 72
  • This link might help: http://ains.co/blog/things-which-arent-magic-flask-part-1.html – Jon Kiparsky Mar 21 '15 at 17:46
  • 1
    If you went through the basics then you should know we can still access it using the name `hello` because the above thing is equivalent to: `hello = @app.route("/")(hello)`. – Ashwini Chaudhary Mar 21 '15 at 17:47
  • But for that to happen I'll need to call `hello()`, which I'm not doing in Flask code – Kartik Anand Mar 21 '15 at 17:48
  • @KartikAnand You don't have to because flask does that for you when the route `'/'` is accessed. Similarly in the second example when you call `hello()` you're actually calling `inner()` which in turn calls the actual `hello()`. – Ashwini Chaudhary Mar 21 '15 at 17:53
  • @AshwiniChaudhary I understand the part wherein calling `hello()` calls the `inner()` and in turn calls the original `hello()`. What I don't understand is how does Flask access the original `hello()` inside `/`. – Kartik Anand Mar 21 '15 at 17:55
  • @KartikAnand It stores them in a dictionary using the url endpoint: https://github.com/mitsuhiko/flask/blob/master/flask/app.py#L65 – Ashwini Chaudhary Mar 21 '15 at 17:59

3 Answers3

5

In decorate, foo is the function you are decorating (in this case, hello). You can store the function in a list or dictionary somewhere, since it's a normal object.

For example:

decorated_functions = []

def decorate(foo):
    def inner():
        print("Some stuff 1")
        foo()
        print("Some stuff 2")

    decorated_functions.append(inner)
    return inner

@decorate
def hello():
    print("Hello")

## The above is the same as:
# def hello():
#     print("Hello")
# hello = decorate(hello)

print(decorated_functions[0] == hello) # prints True
decorated_functions[0]() # prints "Some stuff 1", "Hello", and "Some stuff 2"
hello() # same as above
Colonel Thirty Two
  • 23,953
  • 8
  • 45
  • 85
1

Decorators are actually a very simple construct in Python. The following two examples are equivalent:

@foo
def bar():
     pass

def bar():
     pass

bar = foo(bar)

So your definition of hello in your first example is the equivalent to this:

def hello():
     return "Hello World!"

hello = app.route("/")(hello)

The double function calls might be a bit confusing so lets rewrite it like this:

_tempfn = app.route("/")
hello = _tempfn(hello)

So it should be clear now that app.route isn't actually a decorator, it's a function that creates decorators. Now what isn't obvious is what the newly decorator does. Without looking at the source for Flash, it's likely that it adds the function hello to a dictionary member of a app. So app.route is implemented something like this:

class app(object):
    def route(self, path):
        def decorator(fn):
            self.pathmap[path] = fn
            return fn
        return decorator

Note that this is pretty much a condensed version of the explanation given in the link that Jon Piparsky provided.

Ross Ridge
  • 38,414
  • 7
  • 81
  • 112
1

The decorator is simply registering the function in a lookup table. Decorators don't need to modify a function in any way, there are so many other useful things, decorators can do.

In fact, decorators in python

@some_decorator
def foo():
    pass

are only a short form of

def foo():
    pass

foo = some_decorator(foo)

and the function some_decorator can do anything with its arguments and can return any return value, but usually returns a function.

Daniel
  • 42,087
  • 4
  • 55
  • 81