cython & flask

  1. python的编译

    • pyc:

      • pyc是编译py之后生成的二进制文件,由python虚拟机来执行的

      • 在模块被加载时,.pyc文件比.py文件更快

      • 可直接反编译为源码

      • 生成方式1:

        • 生成pyc文件:python -m py_compile {file1,file2}.py / python -m compileall DIR
        • 删除py文件:find . -name “*.py” |xargs rm -rf
        • 删除pycache目录:find . -name “pycache” |xargs rm -rf
      • 生成方式2:

        1
        2
        3
        4
        5
        6
        7
        # single file
        import py_compile
        py_compile.compile('$file_path')

        # dir
        import compileall
        compileall.compile_dir('$dir')
    • cython编译成动态库.so:

      • pyx文件由cython编译为.c文件,.c文件由C/C++编译器编译为.so动态库
      • 能起到代码保护的作用
      • 但是编译速度太慢了
      • 注意在编译的时候每个库里必须有一个__init__.py文件
      • 生成方式:
        • 创建setup.py
        • 运行python setup.py build_ext
        • 会在执行目录下,新增build文件夹,里面放有.so
  2. python的反编译

    • pyc:
      • 需要安装第三方库:pip install uncompyle6
      • uncompyle6 -o . *.pyc
  3. Flask(https://zhuanlan.zhihu.com/p/32202156)

    • 一些概念

      • API:应用程序接口,由服务器(Server)提供,一般是web server(网络服务器),使得外部机器通过API读取、编辑网站数据,通俗来讲API是一套协议,规定了与外界的沟通方式——如何发送请求和接受响应
      • HTTP动词(GET、POST、PUT、DELETE):它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源
      • 装饰器:放在函数前面,相当于将函数对象传入这个wrapper方法中
    • startup:URL

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      from flask import Flask


      app = Flask(__name__)


      @app.route('/')
      def index():
      return 'Index Page'


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


      @app.route('/user/<string:username>')
      def show_user_profile(username):
      # show the user profile for that user
      return 'User %s' % username


      if __name__ == '__main__':
      # app.run()
      app.run(debug=True)
    * app = Flask(\__name__): 新建一个Flask类的实例,这是一个wsgi(Web服务器网关接口,Python Web Server Gateway Interface)

    * @app.route(URL): 用route装饰器告诉Flask什么样的URL能够触发我们的函数
           - @app.route('/')就代表默认根地址http://127.0.0.1:5000/

           - @app.route('/hello')则代表http://127.0.0.1:5000/hello

           - @app.route('/user/<str: username>')则带了传入变量以及它的变量转换器,e.g. http://127.0.0.1:5000/user/amber

                <img src="cython-flask/转换器.png" width="60%;" />

           - 同时可以看到,每执行一次访问,debug界面生成一次GET请求("GET /hello HTTP/1.1")以及服务器返回的内容(200),具体的解释在下面的章节

    * def index()/hello_world()/show_user_profile(username): 这些函数在生成指定URL时被调用,这些方法都叫做视图函数

    <img src="cython-flask/url.png" width="40%;" />   <img src="cython-flask/get.png" width="40%;" />

* HTTP方法

  * HTTP 方法,通过浏览器告知服务器,客户端想对请求的页面做什么

    * GET(方法)告知服务器:只获取页面上的信息并发给我,这是最常用的方法
    * POST(方法)告诉服务器:想在 URL 上发布新信息。并且服务器必须确保数据已存储且仅存储一次,这是 HTML 表单通常发送数据到服务器的方法
    * PUT(方法)类似 POST 但是服务器可能触发了存储过程多次,多次覆盖掉旧值。你可能会问这有什么用,当然这是有原因的。考虑到传输中连接可能会丢失,在这种情况下浏览器和服务器之间的系统可能安全地第二次接收请求,而不破坏其它东西。因为 POST 它只触发一次,所以用 POST 是不可能的

    * DELETE(方法)删除给定位置的信息

  * 默认情况下,路由只回应GET请求,如上图的GET info,在访问指定URL时候就被触发了,具体方法通过route()装饰器传递methods参数来指定

* 设计一个简单的应用todoList

  * 首先设计一个根URL:如http://[hostname]/todo/api/v1.0/

    * hostname:[ip:port_id]
    * todo:应用名称
    * api/v1.0:API版本

  * 第二步规划数据结构和动作

    * 动作

    | HTTP方法 |                       URL                       |         动作         |
    | :------: | :---------------------------------------------: | :------------------: |
    |   GET    |      http://[hostname]/todo/api/v1.0/tasks      |     检索任务清单     |
    |   GET    | http://[hostname]/todo/api/v1.0/tasks/[task_id] |     检索指定任务     |
    |   POST   |      http://[hostname]/todo/api/v1.0/tasks      |   创建一个新的任务   |
    |   PUT    | http://[hostname]/todo/api/v1.0/tasks/[task_id] | 更新一个已存在的任务 |
    |  DELETE  | http://[hostname]/todo/api/v1.0/tasks/[task_id] |     删除一个任务     |
    |          |                                                 |                      |

    ​    可以看到我们通过指定HTTP method,可以在同一个URL上实现不同的请求

    * 任务的数据结构
        * id:唯一标识。整型。
        * title:简短的任务描述。字符串型。
        * description:完整的任务描述。文本型。
        * done:任务完成状态。布尔值型。

  * 第三步实现第一个方法:get_tasks()

    
1
2
3
4
global tasks
@app.route('/todo/api/v1.0/tasks', methods=['GET'])
def get_tasks():
return jsonify({'tasks': tasks})
通过访问http://127.0.0.1:5000/todo/api/v1.0/tasks可以查看task列表 * 第四步实现【获取指定任务/删除任务】方法:get_task(task_id)/delete_task(task_id)
1
2
3
4
5
6
7
@app.route('/todo/api/v1.0/tasks/<int:task_id>', methods=['GET'])
def get_task(task_id):
pass

@app.route('/todo/api/v1.0/tasks/<int:task_id>', methods=['DELETE'])
def delete_task(task_id):
pass
这两个方法比较类似,都是先查询,存在即执行操作,否则抛出404 * 第五步实现【创建一个新的任务】方法:create_task()
1
2
3
4
5
6
7
8
9
10
11
12
13
@app.route('/todo/api/v1.0/tasks', methods=['POST'])
def create_task():
if not request.json or 'title' not in request.json:
abort(400)
# new task
task = {
'id': tasks[-1]['id'] + 1,
'title': request.json['title'], # not blank
'description': request.json.get('description', ""),
'done': False
}
tasks.append(task)
return jsonify({'task': task}), 201
这里面出现了**request和状态码**,这部分内容放在下一节 * 第六步实现【更新一个任务】方法:update_task(task_id)
1
2
3
4
@app.route('/todo/api/v1.0/tasks/<int:task_id>', methods=['POST'])
def update_task(task_id):
# request.json
pass
* 配置服务器ip和端口号
  1. HTTP请求(https://www.jianshu.com/p/4456b0906708)

    • flask的工作,就是开启一个server,监听client端发出的请求,并作出响应,请求和响应都是以http request的方式

    • 服务器端就是flask开启的web server,客户端通过浏览器向server传递指令

    • 请求报文 request message

      • 请求报文由请求行(HTTP方法、URL、协议版本)、首部字段(header)、空行、请求数据(内容实体)组成

      • request对象

        • 假设请求的url是:http://helloflask.com/hello?name=Grey

          • http://:协议字符串,指定要使用的协议,这里是http协议
          • helloflask.com:服务器的地址(域名)
          • /hello:资源路径(route)
          • ?name=Grey:query string,查询字符串,查询字符串从?开始,以键值对的形式写出,多个键值对之间用&分隔
        • 属性和方法:

          • 上面出现的request.json,就是在获取json格式的报文
          • 本例中执行request.args.get(‘name’, ‘’),就能获取到url请求中name的value——Grey
    • 路由匹配 & server处理

      • 当server能够匹配到http request给出的host/URL和methods的时候,就会调用对应的视图函数,否则得到404

      • 查看当前server定义的所有路由:

        • export FLASK_APP=todo
        • flask routes

        • 每个路由对应:断点(Endpoint)、HTTP方法(Methods)和URL规则(Rule),其中static是flask添加的特殊路由,用来访问静态文件

      • 生成响应

        • 在flask程序中,客户端发出的请求触发响应的视图函数,获取的返回值会作为响应的主体最后生成完整的响应,即响应报文

        • 视图函数可以返回最多由三个元素组成的元组:响应主体、状态码、首部字段

        • 默认的状态码为200

        • 首部字段可以为字典,或是两元素元组组成的列表

        • 一个普通的响应可以只返回主体内容,其余保持默认值(200,{})

        • 在abort()函数中传入状态码即可返回对应的错误响应,不需要return,abort之后的代码将不会被执行

        • Flask中可以调用make_response()方法将视图函数返回值转换为响应对象

          1
          2
          3
          4
          5
          6
          7
          from flask import make_response

          @app.route('/foo')
          def foo():
          response = make_response('Hello, World!')
          response.mimetype = 'text/plain'
          return response
  • 响应报文 response message

    • 视图函数可以返回最多由三个元素组成的元组:响应主体、状态码、首部字段,这就是响应的主体

    • 响应的报文首部包含一些关于响应和服务器的信息,这些内容由Flask生成

    • 浏览器接收到响应后,会将响应主体解析并显示在浏览器窗口上

    • 响应报文主要由协议版本、状态码(status code)、原因短语(reason phrase)、响应首部和响应主体组成

    • 常见HTTP状态码