Flask

Flask 上下文

2018-06-21  本文已影响4人  梦醒家先生
上下文
Python中上下文的定义
class File():
    # File()时调用,相当于创建类对象执行__init__属性
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode

    def __enter__(self):
        print("entering")
        self.f = open(self.filename, self.mode)
        # 返回as 后面的值
        return self.f

    def __exit__(self, *args):
        print("will exit")
        # 退出关闭,释放资源
        self.f.close()
with File('out.txt', 'w') as f:
    print("writing")
    f.write('hello, python')

enter() 方法返回资源对象,这里就是你将要打开的那个文件对象,exit() 方法处理一些清除工作。

因为 File 类实现了上下文管理器,现在就可以使用 with 语句了。
这样,你就无需显示地调用 close 方法了,由系统自动去调用,哪怕中间遇到异常 close 方法也会被调用。

from contextlib import contextmanager

@contextmanager
def my_open(path, mode):
    f = open(path, mode)
    yield f
    f.close()
#调用

with my_open('out.txt', 'w') as f:
    f.write("hello , the simplest context manager")

Python 提供了一个 contextmanager 的装饰器,更进一步简化了上下文管理器的实现方式。通过 yield 将函数分割成两部分,yield 之前的语句在 enter 方法中执行,yield 之后的语句在 exit 方法中执行。紧跟在 yield 后面的值是函数的返回值。

Flask两种上下文

Flask中文文档: This is an Flask中文文档

请求上下文

from flask import request
@app.route('/')
def index():
    user_agent = request.headers.get('User-Agent')
    return '<p>Your browser is %s</p>' % user_agent

Flask中有四种请求hook,分别是@before_first_request @before_request @after_request @teardown_request

from flask import Flask
from flask import json
from werkzeug.routing import BaseConverter  # 进入源代码结构查看routing模块的结构

app = Flask(__name__)


@app.route('/')
def hello_world():
    print('代码正在执行')
    dic = {
        'name': 'ithema'
    }
    return json.dumps(dic)


# 在第一次请求之前会调用
# 应用地方:程序的初始化操作(一些准备工作,数据的链接请求)
@app.before_first_request
def before_first_request():
    print('before_first_request')


# 在每一次请求之前会调用
# 应用:用户的权限验证
@app.before_request
def before_request():
    #  在这里可以做一些判断,这里使用return 后面的都不会执行(视图函数都不会进入)
    print('before_request')
    # if ....
    # return 'hehe'


# 在每次请求执行之后执行
@app.after_request
def after_request(response):
    print('after_request')
    # 这里可以对响应对象做处理
    # 对所有返回的json数据进行处理(所有的都处理掉)
    response.headers["Content-Type"] = "application/json"
    return response


# 在每次请求执行的最后执行,如果服务器出现错误,这里也可以获取
@app.teardown_request
def teardown_request(error):
    # 可以记录错误
    # 以后可能会用到请求数据库的自动提交
    print('teardown_request:%s' % error)


if __name__ == '__main__':
    app.run(debug=True)

如同上面的代码一样,在每个请求上下文的函数中我们都可以访问request对象,然而request对象却并不是全局的,因为当我们随便声明一个函数的时候,比如:

def handle_request():
    print 'handle request'
    print request.url 
if __name__=='__main__':
    handle_request()
# 错误报警
RuntimeError: working outside of request context。

因此可知,Flask的request对象只有在其上下文的生命周期内才有效,离开了请求的生命周期,其上下文环境不存在了,也就无法获取request对象了。而上面所说的四种请求hook函数,会挂载在生命周期的不同阶段,因此在其内部都可以访问request对象。

>>> import threading
>>> storage = threading.local()
>>> storage.foo = 1
>>> print(storage.foo)
1
>>> class AnotherThread(threading.Thread):
...         def run(self):
...             storage.foo = 2
...             print(storage.foo) # 这这个线程里已经修改了
>>>
>>> another = AnotherThread()
>>> another.start()
2
>>> print(storage.foo) # 但是在主线程里并没有修改
1

因此只要有Thread Local对象,就能让同一个对象在多个线程下做到状态隔离。

Flask是一个基于WerkZeug实现的框架,因此Flask的App Context和Request Context是基于WerkZeug的Local Stack的实现。这两种上下文对象类定义在flask.ctx中,ctx.push会将当前的上下文对象压栈压入flask._request_ctx_stack中,这个_request_ctx_stack同样也是个Thread Local对象,也就是在每个线程中都不一样,上下文压入栈后,再次请求的时候都是通过_request_ctx_stack.top在栈的顶端取,所取到的永远是属于自己线程的对象,这样不同线程之间的上下文就做到了隔离。请求结束后,线程退出,ThreadLocal本地变量也随即销毁,然后调用ctx.pop()弹出上下文对象并回收内存。

应用上下文

current_app
应用程序上下文,用于存储应用程序中的变量,可以通过current_app.name打印当前app的名称,也可以在current_app中存储一些变量,例如:

current_app.name
current_app.test_value='value'

g变量
g 作为 flask 程序全局的一个临时变量,充当者中间媒介的作用,我们可以通过它传递一些数据,g 保存的是当前请求的全局变量,不同的请求会有不同的全局变量,通过不同的thread id区别

g.name='abc'
注意:不同的请求,会有不同的全局变量

上一篇下一篇

猜你喜欢

热点阅读