这不是魔法:Flask和@app.route(1)
admin
2023-07-31 01:43:21
0

距离我上一次写文章到现在已经颇有一段时间了,我想差不多也该在博客里开始新的系列了。

本文是我称为「这不是魔法」系列的第一篇,我准备在里面展示一些热门开源包提供的友好API是如何通过它们各自语言的原始语法构造的。

本文我们先来说说Flask,深入探讨Flask如何实现在函数上方写“@app.route()”就能在因特网上输出函数的执行结果。

下面是Flask主页给我们的第一个例子,我们现在就由它入手,深入理解“@app.route()”是如何工作的。

12345 app = Flask(__name__) @app.route(\”/\”)def hello():    return \”Hello World!\”

@app.route和其它装饰器

要想明白“@app.route()”的工作原理,我们首先需要看一看Python中的装饰器(就是以“@”开头的那玩意,下面接着函数定义)。

究竟什么是装饰器?没啥特别的。装饰器只是一种接受函数(就是那个你用“@”符号装饰的函数)的函数,并返回一个新的函数。

当你装饰一个函数,意味着你告诉Python调用的是那个由你的装饰器返回的新函数,而不仅仅是直接返回原函数体的执行结果。

还不是很明白?这里是一个简单的例子:

12345678910111213141516 # This is our decoratordef simple_decorator(f):    # This is the new function we\’re going to return    # This function will be used in place of our original definition    def wrapper():        print \”Entering Function\”        f()        print \”Exited Function\”     return wrapper @simple_decorator def hello():    print \”Hello World\” hello()

运行上述代码会输出以下结果:

Entering Function
Hello World
Exited Function

很好!

现在我们有点明白怎样创建我们自己的“@app.route()”装饰器了,但你可能会注意到有一个不同点,就是我们的simple_decorator不可以接受任何参数, 但“@app.route()”却可以。

那么我们怎样才能给我们的装饰器传参数?要实现这个我们只需创建一个“decorator_factory”函数,我们调用这个函数,返回适用于我们函数的装饰器。现在看看如果实现它。

1234567891011121314151617 def decorator_factory(enter_message, exit_message):    # We\’re going to return this decorator    def simple_decorator(f):        def wrapper():            print enter_message            f()            print exit_message         return wrapper     return simple_decorator @decorator_factory(\”Start\”, \”End\”)def hello():    print \”Hello World\” hello()

给我们的输出是:

Start
Hello World
End
请注意在我们写@decorator_factory(“Start”, “End”)时,我们实际调用的是decorator_factory函数,实际返回的装饰器已经被用上了,代码很整洁,对吧?

把“app”放进“app.route”

现在我们掌握了装饰器怎样工作的全部前置知识 ,可以重新实现Flask API的这个部分了,那么把我们的目光转移到“app”在我们Flask应用中的重要地位上面来。

在开始解释Flask对象里面发生了什么之前,我们先创建我们自己的Python类NotFlask。

1234 class NotFlask():    pass app = NotFlask()

这不是个很有趣的类,不过有一样值得注意,就是这个类的方法也可以被用作装饰器,所以让我们把这个类写得更有趣一点,加一个称作 route的方法,它是一个简单的装饰器工厂。

123456789101112 class NotFlask():    def route(self, route_str):        def decorator(f):            return f         return decorator app = NotFlask() @app.route(\”/\”)def hello():    return \”Hello World!\”

这个装饰器和我们之前创建的那些最大的不同,在于我们不想修改被我们装饰的函数的行为,我们只是想获得它的引用。

所以,最后一步是我们打算去利用一个特性,就是用装饰器函数的副产品去保存一个提供给我们的路径之间的链接,装饰器函数应该与它关联起来。

为了实现这个,我们给我们的NotFlask对象加一个“routes”字典,当我们的“decorator”函数被调用,路径将被插入新字典中函数对应的位置。

12345678910111213141516 class NotFlask():    def __init__(self):        self.routes = {}     def route(self, route_str):        def decorator(f):            self.routes[route_str] = f            return f         return decorator app = NotFlask() @app.route(\”/\”)def hello():    return \”Hello World!\”

现在我们就要完成了!可如果没法访问内部的视图函数,保存路径的字典又有什么用?让我们加入一个方法serve(path),当给定的路径存在时运行一个函数并给们我结果,当路径尚未注册时则抛出一个异常。

1234567891011121314151617181920212223 class NotFlask():    def __init__(self):        self.routes = {}     def route(self, route_str):        def decorator(f):            self.routes[route_str] = f            return f         return decorator     def serve(self, path):        view_function = self.routes.get(path)        if view_function:            return view_function()        else:            raise ValueError(\’Route \”{}\”\” has not been registered\’.format(path)) app = NotFlask() @app.route(\”/\”)def hello():    return \”Hello World!\”

在这个系列我们只关注重现那些热门库提供的友好API,所以钩挂“serve”方法实现一个HTTP服务器其实有一点超出本文的范围,当然结果是确定的,运行下述片段:

1234567 app = NotFlask() @app.route(\”/\”)def hello():    return \”Hello World!\” print app.serve(\”/\”)

我们会看到:

Hello World!

我们已经完成了一个的Flask网页上第一个例子的非常简单的重现,让我们写一些快速测试检测我们简单重现的Flask的“@app.route()”是否正确。

1234567891011121314 class TestNotFlask(unittest.TestCase):    def setUp(self):        self.app = NotFlask()     def test_valid_route(self):        @self.app.route(\’/\’)        def index():            return \’Hello World\’         self.assertEqual(self.app.serve(\’/\’), \’Hello World\’)     def test_invalid_route(self):        with self.assertRaises(ValueError):            self.app.serve(\’/invalid\’)

吸口气。

完全正确!所以,仅仅是一个简单的包含一个字典的装饰器, 就重现了Flask的“app.route()”装饰器的基本的行为。

在本系列的下一篇,也是Flask的app.route()的最后一篇,将通过解析下面这个例子来解释动态URL模式是如何工作。

12345 ; htmlscript: false ]app = Flask(__name__) @app.route(\”/hello/\”)def hello_user(username):    return \”Hello {} !\”.format(username)

待续!

相关内容

热门资讯

500 行 Python 代码... 语法分析器描述了一个句子的语法结构,用来帮助其他的应用进行推理。自然语言引入了很多意外的歧义,以我们...
Mobi、epub格式电子书如... 在wps里全局设置里有一个文件关联,打开,勾选电子书文件选项就可以了。
定时清理删除C:\Progra... C:\Program Files (x86)下面很多scoped_dir开头的文件夹 写个批处理 定...
scoped_dir32_70... 一台虚拟机C盘总是莫名奇妙的空间用完,导致很多软件没法再运行。经过仔细检查发现是C:\Program...
65536是2的几次方 计算2... 65536是2的16次方:65536=2⁶ 65536是256的2次方:65536=256 6553...
小程序支付时提示:appid和... [Q]小程序支付时提示:appid和mch_id不匹配 [A]小程序和微信支付没有进行关联,访问“小...
pycparser 是一个用... `pycparser` 是一个用 Python 编写的 C 语言解析器。它可以用来解析 C 代码并构...
微信小程序使用slider实现... 众所周知哈,微信小程序里面的音频播放是没有进度条的,但最近有个项目呢,客户要求音频要有进度条控制,所...
Apache Doris 2.... 亲爱的社区小伙伴们,我们很高兴地向大家宣布,Apache Doris 2.0.0 版本已于...
python清除字符串里非数字... 本文实例讲述了python清除字符串里非数字字符的方法。分享给大家供大家参考。具体如下: impor...