Python Python-Flask 入门及基本原理分析

流浪豆 · 2023年02月12日 · 最后由 小鸭子嘎嘎 回复于 2023年02月13日 · 7142 次阅读

背景

​ 团队准备开发 QA 工具平台,一方面锻炼编程能力,一方面是提高团队的工作效率,如可测性、数据构造等(避免仅仅是写一些重复的接口用例)。团队成员的现状如下:

  • 比较熟悉的是Python语言;
  • 以前基本没有工具平台的开发经验。

根据上述的背景来看,在进行整体技术栈选型的时候以下两点比较重要。

  • 上手速度要快;
  • 社区比较活跃,相关文档比较多。

框架选型

在进行后端框架的选型之前,先简单的对框架进行一些了解。

框架(Framework)是一个框子——指其约束性,也是一个架子——指其支撑性

它本身一般不完整到可以解决特定问题,其主要的特性是为了扩展各种功能已满足用户的需求。

框架选择

Python 的常用 Web 框架挺多,但在 Github 上比较火的有 DjangoFlaskTornado 与前两者的 star 数量基本已经相差好几倍了)。

  • Django 一一 企业级开发框架

诞生于 2003 年,相对于其他 WEB 框架来说,它主要优点是功能齐全,缺点则是模块之间紧密耦合,开发者需要学习 Django 自己定义的这一整套技术。

  • Flask 一一 快速建站

诞生于 2010 年,其定义就是面向简单需求和小型应用的微框架 (啥叫微框架,就是毛坯房的意思。给你个毛胚房,你自己装修去),这样的好处就是具有较高的扩展性。

  • Tornado 一一 高并发处理

诞生于2009年9月10日,其主要特点是:提供了异步 I/O 支持、超时事件处理。

  • Twisted、Pyramid、Web2py 等

不同框架的区别

  • Full-Stack Web 框架:框架功能很全,不用自己造轮子,比如 (cache、session、登陆、auth 授权等等) 以及它强大的中间件,提供全方案 Web 开发支持。比如Django 框架

    当然功能强大和全面的反面就是有点复杂(相对的),不太灵活。所以 Django 上手要慢一点,自己造一个轮子替换 Django 某些内置功能或者使用第三方功能时不太灵活。

  • Non Full-Stack Web 框架:框架小巧,灵活,很多功能需要开发者以插件的形式向里安装,也可以自己定制,比如Flask 框架

  • Asynchronous 异步框架:框架本身的速度比较快,I/O 性能吞吐高并发,当然异步编程的理解难度要大一点。像TornadoSanic 框架

为什么选择 Flask

      截至2019年9月2日,Flask 在 Github 上的星数是 46179 颗,Django 的 Github 星数是 43806 颗,两者几乎难分伯仲,其它 Python Web 框架与 Flask 和 Django 星数相差甚远。考虑到 Django 早发布 5 年,而 Flask 在星数上还领先 2000 多颗,由此可以得知 Flask 当前略微占优。

选 Flask 框架的原因主要基于如下几个点:

  1. 简约的设计哲学 一一 Less is more

    from flask import Flask
    app = Flask(__name__)
    
    @app.route('/')
    def hello_world():
        return 'Hello World!'
    
    if __name__ == '__main__':
        app.run()
    
  2. 通俗易懂的 快速入门

  3. 装饰器表示路由。(面向切面编程-JAVA 中的 AOP)。

    @app.route('/')
    def hello_world():
        return 'Hello World!'
    
    @app.route('/add', methods=['POST'])
    def compute_add():
        params = request.get_json().get('params')
        if not params or not isinstance(params, dict):
            return "params is error"
        else:
            return str(params.setdefault('a', 0) + params.setdefault('b', 0))
    

    通过本地 Debug 可以明显发现,代码启动后自动通过 app.route('xxx') 就自动将前端访问的路由地址与后端的代码给绑定到了一起,并且在后续的请求中,通过装饰器自动定位到具体的后端方法。

  4. 蓝图 一一 Blueprint

    常见的情况:当功能多了后就会出现如下代码的样式,很多个路由都在一个文件中,维护以及可读性都很差。这个时候就需要方便的管理这些路由。

    @app.route('/add')
    def hello_world1():
        return 'Hello World!'
    
    @app.route('/user_info')
    def hello_world2():
        return 'Hello World!'
    
    @app.route('/compute')
    def hello_world3():
        return 'Hello World!'
    
    ...
    

    蓝图便类似一个有效的菜单栏,通过将不同的功能的子菜单在不同的文件中单独维护,然后集中在通过 app.register\_blueprint(xxx) 注册到代码中。

    from flask import Blueprint, jsonify
    
    compute_method_api = Blueprint('compute_method', __name__)
    
    @user7oute("/compute_add")
    def get_sonar_error_info():
        return jsonify(1 + 1)
    

通过上面介绍,在需要开发的平台不太复杂的情况下,Flask比较符合新手作为快速入门的框架(个人意见)。

Flask 框架介绍

​ Flask 是一种使用 Python 编写的轻量级的 Web 框架,WSGI 工具采用 Werkzeug,模板引擎使用 Jinja2,下图表示 WSGI 的一个过程。




首先从官网的一个最小的 Flask 应用进行分析,运行如下代码:
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

(在不去了解内部原理的情况下)光从代码来看,仅仅做了如下几点:

  1. 首先,导入了 Flask 类,这个类的对象将会是我们的 WSGI 应用程序。
  2. 接下来,创建一个该类的实例对象,第一个参数是应用模块或者包的名称。这样 Flask 才知道到哪去找模板、静态文件等等。详情见 Flask 的文档。
  3. 然后,我们使用 route() 装饰器告诉 Flask 什么样的 URL 能触发我们的函数。这个函数的名字也在生成 URL 时被特定的函数采用,这个函数返回我们想要显示在用户浏览器中的信息。
  4. 最后我们用 run() 函数来让应用运行在本地服务器上。

那具体上面的 Demo 代码实现原理是如何呢?具体分为 3 个步骤,分别对应着上面的代码层面的逻辑: 见如下图:



  • 实例一个 Flask 类的对象,里面进行了各种参数的初始化,主要讲下里面如下几个参数:
  1. self.static_url_path:用于改变 url 的 path,默认静态文件放在 static 下面,所以 url 是 static/filename 。

    app = Flask(__name__, static_url_path="/static/change")
    # 如上指定static_url_path路径后那么默认的static/filename就访问不到静态资源了,必须通过static/change/filename才能访问到。
    
  2. self.static_folder: 用来改变静态资源目录,默认是 static 目录下。

    app = Flask(__name__, static_folder="my_static")
    # static_folder="my_static" 那么默认的static/filename就访问不到静态资源了,必须通过my_static/filename才能访问到静态资源。
    
  3. self.blueprints: 用于更好的管理多个 Api 文件。(更多相关可以自行搜索Flask 蓝图

    举个例子:如果把所有的路由都如 Demo 代码里面的 app.route 一样写到一个文件中,那随着项目的增加,此文件的维护性会越来越差,所以通过先将不同的模块都分别写到对应的文件中,然后通过蓝图的注册到 Flask 实例对象中的self.blueprints

  4. self.url_map: 用于存储所有的路由规则。

  • @app.route('xxx') 进行所有路由规则进行添加。

基本的原理就是通过解析所有带有route装饰器的方法,通过将 rulefunction_name 进行一一绑定并且存储到self.url_map中,大概的存储格式如下图:(rule 为 route('xxx') 中的 xxx, endpoint 为绑定的方法名,像 Demo 代码中 rule 为 '/', endpoint 为 'hello_world')



  • app.run(host='0.0.0.0', port=5000) 启动应用。

从源码中发现 flask 启动 server 的时候实际就是直接使用的 Werkzeug.serving 的 run_simple(...),在这里可以简单的理解内部启动了一个 BaseWSGIServer。

在浏览器中输入 http://0.0.0.0:5000/ 就会返回 Hello World 了。那 Flask 又是如何处理的浏览器发起的请求的呢?大概原理如下图:



整个处理请求的逻辑简单的可以看分为两部分:

  • Werkzeug.serving

简单点说就是创建一个线程,并且为此次请求实例化一个通用处理器 BaseRequestHandler 用于初始化此次请求的信息等,然后直接调用 Flask.app 去执行该请求,最终通过 start_response 对结果进行相应处理后返回。

  • Flask.app

    • Full_dispatch_request: 对请求进行预处理(如蓝图)以及结果的格式化。
    • Dispatch_request: 根据 self.view_functions 里面的 Rule 找到指定的 Function,然后执行 func 返回结果。
    • Make_response:将 dispatch_request 处理完的返回值变成符合 http 协议的响应结果。
try:
  request_started.send(self)
  rv = self.preprocess_request()
  if rv is None:
    rv = self.dispatch_request()
except Exception as e:
  rv = self.handle_user_exception(e)
return self.finalize_request(rv)

下图梳理了 Flask 框架的一个整体流程图(有兴趣的可以更深入的去了解下相关内容)。

分享内容_photo_2.png

参考链接

共收到 4 条回复 时间 点赞

值得学习

不过,我想知道,现在测试开发中,要求掌握 Flask 框架多一点,还是 Django 框架多一点?

(具体数据没去调研过,这边不做太多评论)
个人感觉:没有最好的知识, 优先掌握能解决实际工作中问题的知识最好,毕竟时间有限或者你能卷😂

流浪豆 回复

哈哈,想转测试开发,,已经开始学完 Vue2.0 了,,还没想好,学什么后端,来做自动化测试平台

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册