文章目录
- 1. Local对象隔离线程间的对象,threadlocal变量
- 2. app 应用上下文详解
- 3. request 请求上下文详解
- 4. 线程隔离的g对象使用详解
1. Local对象隔离线程间的对象,threadlocal变量
- local 对象:在Flask中,类似于
request
对象,其实是绑定到了一个werkzeug.local.Local
对象上。
这样,即使是同一个对象,那么在多个线程中都是隔离的。类似的对象还有
from werkzeug.local import Local
# flask = werkzeug + sqlalchemy + jinja2
- ThreadLocal变量:Python提供了ThreadLocal 变量,它本身是一个全局变量,但是每个线程却可以利用它来保存属于自己的私有数据,这些私有数据对其他线程也是不可见的。
- 总结:只要满足绑定到"local"或"Local"对象上的属性,在每个线程中都是隔离的,那么他就叫做
ThreadLocal
对象,也叫’ThreadLocal’变量。 - 代码演示:
- python基础提供的local:
from threading import Thread,local
local =local()
local.request = '具体用户的请求对象'
class MyThread(Thread):
def run(self):
local.request = 'haha'
print('子线程:',local.request)
mythread = MyThread()
mythread.start()
mythread.join()
print('主线程:',local.request)
- Flask中提供的Local:
from werkzeug.local import Local
local = Local()
local.request = '具体用户的请求对象'
class MyThread(Thread):
def run(self):
local.request = 'tantan'
print('子线程:',local.request)
mythread = MyThread()
mythread.start()
mythread.join()
print('主线程:',local.request)
2. app 应用上下文详解
app上下文,也叫应用上下文。
- 应用上下文:应用上下文是存放到一个
LocalStack
的栈中。和应用app相关的操作就必须要用到应用上下文,比如通过current_app
获取当前的这个app
名字。
- 注意 01:
在视图函数中,不用担心应用上下文的问题。因为视图函数要执行,那么肯定是通过访问url的方式执行的,
那么这种情况下,Flask底层就已经自动的帮我们把应用上下文都推入到了相应的栈中。 - 注意 02:
如果想要在视图函数外面执行相关的操作,比如获取当前的app名称,那么就必须要手动推入应用上下文: - 第一种方式:便于理解的写法
from flask import Flask,current_app
app = Flask(__name__)
# app上下文
app_context = app.app_context()
app_context.push()
print(current_app.name)
@app.route('/')
def hello_world():
print(current_app.name) # 获取应用的名称
return 'Hello World!'
if __name__ == '__main__':
app.run(debug=True)
- 第二种方式:用
with
语句【推荐】
from flask import Flask,current_app
app = Flask(__name__)
# app上下文
# 换一种写法
with app.app_context():
print(current_app.name)
@app.route('/')
def hello_world():
print(current_app.name) # 获取应用的名称
return 'Hello World!'
if __name__ == '__main__':
app.run(debug=True)
- 图解
3. request 请求上下文详解
- 一个项目只有一个应用上下文,但是可以有多个请求上下文。
- 请求上下文:
请求上下文也是存放到一个LocalStack
的栈中。
和请求相关的操作就必须用到请求上下文,比如使用url_for
反转视图函数。
- 注意01:
在视图函数中,不用担心请求上下文的问题。
因为视图函数要执行,那么肯定是通过访问url的方式执行的,那么这种情况下,Flask底层就已经自动的帮我们把应用上下文和请求上下文都推入到了相应的栈中。 - 注意02:
如果想要在视图函数外面执行相关的操作,比如反转url,那么就必须要手动推入请求上下文:
- 底层代码执行说明:
* 推入请求上下文到栈中,会首先判断有没有应用上下文。
* 如果没有那么就会先推入应用上下文到栈中。
* 然后再推入请求上下文到栈中
from flask import Flask, url_for
app = Flask(__name__)
# 请求上下文
@app.route('/')
def hello_world():
#和请求相关的操作就必须用到请求上下文,比如使用`url_for`反转视图函数。构建一个url=/mylist/
print(url_for('mylist'))
return 'Hello World!'
@app.route('/mylist/')
def mylist():
return '我的列表'
# 如果想要在视图函数外面执行相关的操作,比如反转url,那么就必须要手动推入请求上下文:
# print(url_for('mylist'))
# RuntimeError: Attempted to generate a URL without the application context being pushed.
# This has to be executed when application context is available.
# with app.app_context():
# print(url_for('mylist'))
# 报错
# RuntimeError: Application was not able to create a URL adapter for request independent URL generation.
# You might be able to fix this by setting the SERVER_NAME config variable.
# 查看源代码 url_for()
with app.test_request_context():
# 底层:
# 推入请求上下文到栈中,会首先判断有没有应用上下文,
# 如果没有那么就会先推入应用上下文到栈中,
# 然后再推入请求上下文到栈中
print(url_for('mylist'))
if __name__ == '__main__':
app.run(debug=True)
- 图解:
- 总结:为什么上下文需要放在栈中?
- 应用上下文:Flask底层是基于werkzeug,werkzeug是可以包含多个app的,所以这时候用一个栈来保存。
如果你在使用app1,那么app1应该是要在栈的顶部,如果用完了app1,那么app1应该从栈中删除。方便其他代码使用下面的app。 - 如果在写测试代码,或者离线脚本的时候,我们有时候可能需要创建多个请求上下文,这时候就需要存放到一个栈中了。使用哪个请求上下文的时候,就把对应的请求上下文放到栈的顶部,用完了就要把这个请求上下文从栈中移除掉。
4. 线程隔离的g对象使用详解
- 保存为全局对象g对象的好处:
- g对象是在整个Flask应用运行期间都是可以使用的。
- 并且也跟request一样,是线程隔离的。
- 这个对象是专门用来存储开发者自己定义的一些数据,方便在整个Flask程序中都可以使用。
- 一般使用就是,将一些经常会用到的数据绑定到上面,以后就直接从g上面取就可以了,而不需要通过传参的形式,这样更加方便。
- g对象使用场景:有一个工具类 utils.py 和 用户办理业务
【30_g_object_demo.py】
from flask import Flask,g,request
from utils import funa,funb,func
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
# g对象使用场景:有一个工具类utils.py 和 用户办理业务
# @app.route("/profile/")
# def my_profile():
# #从url中取参
# uname = request.args.get('uname')
#
# #调用功能函数办理业务
# funa(uname)
# funb(uname)
# func(uname)
#
# #每次都得传参 麻烦,引入g对象进行优化
# return "办理业务成功"
# Flask_线程隔离的g对象使用详解
@app.route("/profile/")
def my_profile():
#从url中取参
uname = request.args.get('uname')
# 调用功能函数办理业务
# funa(uname)
# funb(uname)
# func(uname)
#每次都得传参 麻烦,引入g对象进行优化
g.uname = uname
funa()
funb()
func()
return "办理业务成功"
if __name__ == '__main__':
app.run(debug=True)
【utils.py】
# 工具类
# def funa(uname):
# print('funa %s' % uname)
#
# def funb(uname):
# print('funb %s' % uname)
#
# def func(uname):
# print('func %s' % uname)
from flask import g
def funa():
print('funa %s' % g.uname)
def funb():
print('funb %s' % g.uname)
def func():
print('func %s' % g.uname)