Flask-上下文管理
一、threading-local
1、threding-local
作用:为每一个线程开辟一块空间进行数据存储
from threading import local from threading import Thread import time # 示例化local对象 ret=local() def task(s): global ret ret.value=s time.sleep(2) print(ret.value) # 开启10个线程 for i in range(10): t=Thread(target=task,args=(i,)) t.start()
2、自定义local
# 如果有协程则使用协程唯一标识getcurrent
try:
from greenlet import getcurrent as get_ident
except Exception as e:
from threading import Thread,get_ident
class Local(object):
# 线程的唯一标识
ident = get_ident()
def __init__(self):
# 执行父类__setattr__
object.__setattr__(self,"storage",{})
def __setattr__(self,k, v):
"""
构造dict
storage={
ident:{val:0},
ident:{val:1},
ident:{val:3},
ident:{val:4},
}
"""
if self.ident in self.storage:
self.storage[self.ident][k] = v
else:
self.storage[self.ident] = {k: v}
def __getattr__(self,k):
return self.storage[self.ident][k]
obj=Local()
def task(arg):
# 执行__setattr__
obj.var=arg
# 执行__getattr__
v=obj.var
print(v)
for i in range(10):
t = Thread(target=task,args=(i,))
t.start()
二、上下文管理源码分析
1、上下文管理本质(类似于threading.local)
1、每一个线程都会在Local类中创建一条数据
{
“唯一标识”:{stark:[ctx,]}
“唯一标识”:{stark:[ctx,]}
}
2、当请求进来之后,将请求相关数据添加到列表里面[request,],以后如果使用时,就去读取 3、列表中的数据,请求完成之后,将request从列表中移除
2、在源码中分析上下文管理 第一阶段:执行__call__--->app.wsgi-->将ctx(request,session)封装为RequestContent()在(open_session), app_ctx(g,app)封装为APPContent()通过LocalStack将这两个类放入Local对象中 第二阶段:视图函数导入:request/session/g/app ,通过偏函数(_lookup_req_object)在通过(LocalProxy())去LocalStack中的Local类中对其进行增删改查操作
第三阶段:请求处理完毕
- 通过save_session将签名session保存到cookie
-通过ctx.pop()去LocalStack中的Local类- 将ctx删除
有关面试问题
问题一:flask和django的区别:
对于django来说,内部组件特别多,自身功能强大,有点大而全,而flask,内置组件很少,但是它的第三方组件很多,扩展性强,有点短小精悍,而它们之间也有相似之处,
因为它们两个框架都没有写sockte,都是基于wsgi协议做的,在此之外,flask框架中的上下文管理较为耀眼。
相同点:它们两个框架都没有写sockte,都是基于wsgi协议做的
请求相关数据传递的方式不同:django:通过传递request参数取值
flask:见问题二
组件不同:django组件多
flask组件少,第三方组件丰富
问题1.1: flask上下文管理:
简单来说,falsk上下文管理可以分为三个阶段:
1、请求进来时,将请求相关的数据放入上下问管理中
2、在视图函数中,要去上下文管理中取值
3、请求响应,要将上下文管理中的数据清除
详细点来说:
1、请求刚进来,将request,session封装在RequestContext类中,app,g封装在AppContext类中,并通过LocalStack将requestcontext和appcontext放入Local类中
2、视图函数中,通过localproxy--->偏函数--->localstack--->local取值
3、请求相应时,先执行save.session()再各自执行pop(),将local中的数据清除
问题1.2 flask第三方组件
flask:
-flask-session 默认放入cookie,可以放入redis
-flask-redis
-flask-migrate
-flask-script
-blinker 信号
公共: DBUtils 数据库连接池
wtforms 表单验证+生成HTML标签
sqlalchemy
自定义:Auth 参考falsk-login
问题二:Flask中的session是什么时候创建,什么时候销毁的?
当请求进来时,会将requset和session封装为一个RequestContext对象,通过LocalStack将RequestContext放入到Local对象中,因为
请求第一次来session是空值,所以执行open_session,给session(uuid4())赋值,再通过视图函数处理,请求响应时执行save.session,将签名session写入cookie中,再讲Local中的数值pop掉。
问题三:flask中一共有几个LocalStack和Local对象
两个LocalStack,两个Local
request、session共同用一个LocalStack和Local
g、app共同用一个Localstack和Local
问题四: 为什么把请求放到RequestContext中:
因为request和session都是在视图中操作频繁的数据,也是用户请求需要用的数据,将request和session封装在RequestContext中top,pop一次就可以完成,而单独不封装在一起就会多次操作,
ctx = RequestContext(request,session)
问题五:local作用
-保存 请求上下文对象和app上下文对象
-localstack的源码与threading.local(线程处理)作用相似,不同之处是Local是通过greenlet(协程)获取唯一标识,粒度更细
问题六:Localstack作用
2、将local对象中的数据维护成一个栈【ctx,ctx】(先进后出)
{
“协程或线程的唯一标识”: { stack:[ctx,ctx,ctx,] }
}
为什么维护成一个栈?
当是web应用时:不管是单线程还是多线程,栈中只有一个数据
- 服务端单线程:
{
111:{stack: [ctx, ]}
}
- 服务端多线程:
{
111:{stack: [ctx, ]}
112:{stack: [ctx, ]}
}
离线脚本:可以在栈中放入多个数据
with app01.app_context():
print(current_app)
with app02.app_context():
print(current_app)
print(current_app)
问题七:什么是g?
g 相当于一次请求的全局变量,当请求进来时将g和current_app封装为一个APPContext类,在通过LocalStack将Appcontext放入Local中,取值时通过偏函数,LocalStack、loca l中取值,响应时将local中的g数据删除:
问题八:怎么获取Session/g/current_app/request
通过 、偏函数(lookup_req_object)、Localstack、Local取值
问题九: 技术点:
- 反射 (LocalProxy())
- 面向对象,封装:RequestContext
- 线程(threading.local)
- 笔试:自己写一个类+列表 实现栈。(LocalStack)
问题十:python基本哪些内容比较重要:
1、反射
-CBV
-django配置文件
-wtforms中的Form()示例化中 将"_fields中的数据封装到From类中"
2、装饰器 (迭代器,生成器)
-flask:路由、装饰器
-认证
-csrf
3、面向对象
-继承、封装、多态(简单描述)
-双下划线:
__mro__ wtform中 FormMeta中继承类的优先级
__dict__
__new__ ,实例化但是没有给当前对象
wtforms,字段实例化时返回:不是StringField,而是UnboundField
rest frawork many=Turn 中的序列化
__call__
flask 请求的入口app.run()
字段生成标签时:字段.__str__ => 字段.__call__ => 插件.__call__
__iter__ 循环对象是,自定义__iter__
wtforms中BaseForm中循环所有字段时定义了__iter__
metaclass
- 作用:用于指定当前类使用哪个类来创建
- 场景:在类创建之前定制操作
示例:wtforms中,对字段进行排序。