Flask上下文管理源码--亲自解析一下

时间:2022-06-11 15:05:13

前戏

偏函数

 def index(a,b):
return a+b # 原来的调用方法
# ret=index(1,2)
# print(ret) # 偏函数--帮助开发者自动传递参数
import functools
new_func=functools.partial(index,666)
ret=new_func(1)
print(ret) #结果 667

执行父类方法

 class Base(object):
def func(self):
print('Base.func') class Foo(Base):
def func(self):
# 方式一:根据mro的顺序执行对应方法
# super().func()
# 方式二:主动执行Base方法
Base.func(self)
print('Foo.func') obj=Foo()
obj.func()

面向对象中特殊方法

class Foo(object):
def __init__(self):
object.__setattr__(self,'storage',{}) def __setattr__(self, key, value):
print(key,value,self.storage) obj=Foo()
obj.xx=123 # __getattr__和__setattr__:当给对象创建属性时,会自动执行__setattr__方法,可以将 函数__setattr__方法放到__init__中

用列表实现一个栈

class Stack(object):
def __init__(self):
self.data=[] def push(self,val):
self.data.append(val) def pop(self):
return self.data.pop() def top(self):
return self.data[-1] stack=Stack()
stack.push('杜举飞')
stack.push('杜太平')
stack.push('杜晨飞') print(stack.pop())
print(stack.pop()) print(stack.top()) #打印第一个进去的元素
# 打印结果:后打印我,是一个先进后出的

slots

class Foo(object):
__slots__=('name')
def __init__(self):
self.name='alex'
self.age='' obj=Foo()
print(obj.name) # 只打印name为alex
print(obj.age) #只打印age报错!!
# __slots__表示对外公开的属性,若括号中只有name,表示外部只能调用name字段

Threading.local

# 多个线程对同一个值,进行修改,如何给每个线程开辟一个内存?
import threading
import time
v=0
def task(i):
global v
v=i
time.sleep(2)
print(v) for i in range(10):
t=threading.Thread(target=task,args=(i,))
t.start()
# 执行结果:9 9 9 9 9 9 9 9 9 9

多个线程对同一个值进行修改

# 给每一个线程开辟一块内存
import threading
import time
from threading import local obj=local() def task(i):
obj.xxxxx=i
time.sleep(2)
print(obj.xxxxx,i) for i in range(10):
t=threading.Thread(target=task,args=(i,))
t.start() # 执行结果:
# 0 0
# 1 1
# 2 2
# 5 5
# ...
# 9 9

Threading.local给每一个线程开辟一块内存

# threading.get_ident()功能和threading.local一样,
# 都是为每个线程开辟一个隔离的内存空间
import time
import threading
'''
{
ident:{'xxxx':i}
}
'''
dic = {}
def task(i):
ident = threading.get_ident()
if ident in dic:
dic[ident]['xxx'] = i
else:
dic[ident] = {'xxx': i}
time.sleep(2)
print(dic[ident]['xxx'], i) for i in range(10):
t = threading.Thread(target=task, args=(i,))
t.start()

threading.get_ident()也可以给每个线程开辟一块内存空间,效果和local一样

# 为协程开辟一个隔离的内存空间
import time
import threading
import greenlet
'''
{
ident:{'xxxx':i}
}
'''
dic = {}
def task(i):
ident = greenlet.getcurrent()
if ident in dic:
dic[ident]['xxx'] = i
else:
dic[ident] = {'xxx': i}
time.sleep(2)
print(dic[ident]['xxx'], i) for i in range(10):
t = threading.Thread(target=task, args=(i,))
t.start()

为协程开辟一个隔离的内存空间

Local

Local主要帮我们给线程/协程开辟一块内存空间

try:
from greenlet import getcurrent as get_ident # 创建协程的唯一标识
except:
from threading import get_ident # 创建线程的唯一标识 # self.storage 为{ } # self.storage添加完内容后为 {23334:{'alex':12}} class Local(object):
def __init__(self):
object.__setattr__(self, 'storage', {}) def __setattr__(self, key, value):
ident = get_ident()
if ident not in self.storage: # 如果线程的唯一表示不再字典中
self.storage[ident] = {key: value} # 则在{ }创建一个字典 {23423:"alex":123}
else:
self.storage[ident][key] = value def __getattr__(self, item):
ident = get_ident()
if ident in self.storage:
return self.storage[ident].get(item) # item表示alex # 创建一个字典,线程/协程的唯一标识为键,小字典为值! {22234:{'alex':12}}

自己写的Local

try:
from greenlet import getcurrent as get_ident
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident class Local(object):
__slots__ = ('__storage__', '__ident_func__') def __init__(self):
object.__setattr__(self, '__storage__', {})
object.__setattr__(self, '__ident_func__', get_ident) #self.__ident_func__() 相当于slef.get_ident() def __getattr__(self, name):
try:
print('---执行getattr------')
return self.__storage__[self.__ident_func__()][name] except KeyError:
raise AttributeError(name) def __setattr__(self, name, value):
ident = self.__ident_func__()
storage = self.__storage__
print("-----执行setattr----")
try:
# ident=22344 name=age value=12 即:{22344:{'alex':12}}
storage[ident][name] = value
except KeyError:
storage[ident] = {name: value} def __delattr__(self, name):
try:
del self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name) obj=Local()
obj.age=12 #设置属性时,自动调用__setattr__方法,print(obj.age) 时自动调用__getattr__方法
print(obj.age) # # 结果:
# -----set----
# ---get------
#

源码中的Local

LocalStack

由于Local给我们的线程/协程开辟好了内存空间,当往里边存取数据时使用append、pop存取少麻烦,于是有了LocalStack;

LocalStack帮助我们在给线程/协程开辟的内存空间中,将列表维护成一个栈。

try:
from greenlet import getcurrent as get_ident
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident import functools
class Local(object):
__slots__ = ('__storage__', '__ident_func__') def __init__(self):
object.__setattr__(self, '__storage__', {})
object.__setattr__(self, '__ident_func__', get_ident) # self.__ident_func__() 相当于slef.get_ident() def __getattr__(self, name):
try:
print('---执行getattr------')
return self.__storage__[self.__ident_func__()][name] except KeyError:
raise AttributeError(name) def __setattr__(self, name, value):
ident = self.__ident_func__()
storage = self.__storage__
print("-----执行setattr----")
try:
# ident=22344 name=age value=12 即:{22344:{'alex':12}}
storage[ident][name] = value
except KeyError:
storage[ident] = {name: value} def __delattr__(self, name):
try:
del self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name) # 使用local为线程开辟了一个内存空间后,往内存空间中放数据时,可以使用append,但是比较麻烦,源码帮我们提供了方法,即LocalStack这个类
# LocalStack帮我们将列表维护成一个栈
# 往线程的栈中存放数据--自己写
'''
__storage__={
123121:{ ''stack:[] }
}
'''
# obj = Local()
# obj.stack = []
# obj.stack.append('老杜')
# obj.stack.append('小杜')
# print(obj.stack)
# print(obj.stack.pop())
# print(obj.stack) # 往线程的栈中存放数据--源码提供的
class LocalStack(object):
def __init__(self):
self._local=Local() def push(self, obj):
rv = getattr(self._local, 'stack', None)
if rv is None:
self._local.stack = rv = []
rv.append(obj)
return rv def pop(self):
stack = getattr(self._local, 'stack', None)
if stack is None:
return None
elif len(stack) == 1:
return stack[-1]
else:
return stack.pop() def top(self):
try:
return self._local.stack[-1]
except (AttributeError, IndexError):
return None xxx=LocalStack() class RequestContext(object):
def __init__(self):
self.request='xx'
self.session='oo'
ctx=RequestContext()
xxx.push(ctx)
'''
__storage__={
12312:{stack:[ctx(session/request),]}
}
'''
def get_request_or_session(arg):
ctx=xxx.top()
return getattr(ctx,arg) request=functools.partial(get_request_or_session,'request')
session=functools.partial(get_request_or_session,'session') print(request())
print(session())

自己写的LocalStack

 try:
from greenlet import getcurrent as get_ident
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident import functools
class Local(object):
__slots__ = ('__storage__', '__ident_func__') def __init__(self):
object.__setattr__(self, '__storage__', {})
object.__setattr__(self, '__ident_func__', get_ident) # self.__ident_func__() 相当于slef.get_ident() def __getattr__(self, name):
try:
print('---执行getattr------')
return self.__storage__[self.__ident_func__()][name] except KeyError:
raise AttributeError(name) def __setattr__(self, name, value):
ident = self.__ident_func__()
storage = self.__storage__
print("-----执行setattr----")
try:
# ident=22344 name=age value=12 即:{22344:{'alex':12}}
storage[ident][name] = value
except KeyError:
storage[ident] = {name: value} def __delattr__(self, name):
try:
del self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name) # 使用local为线程开辟了一个内存空间后,往内存空间中放数据时,可以使用append,但是比较麻烦,源码帮我们提供了方法,即LocalStack这个类
# LocalStack帮我们将列表维护成一个栈
# 往线程的栈中存放数据--自己写
'''
__storage__={
123121:{ ''stack:[] }
}
'''
# obj = Local()
# obj.stack = []
# obj.stack.append('老杜')
# obj.stack.append('小杜')
# print(obj.stack)
# print(obj.stack.pop())
# print(obj.stack) # 往线程的栈中存放数据--源码提供的
class LocalStack(object):
def __init__(self):
self._local=Local() def push(self, obj):
rv = getattr(self._local, 'stack', None)
if rv is None:
self._local.stack = rv = []
rv.append(obj)
return rv def pop(self):
stack = getattr(self._local, 'stack', None)
if stack is None:
return None
elif len(stack) == 1:
return stack[-1]
else:
return stack.pop() def top(self):
try:
return self._local.stack[-1]
except (AttributeError, IndexError):
return None _request_ctx_stack=LocalStack() # RequestContext类帮我们往键为 stack的字典存值
class RequestContext(object):
def __init__(self):
self.request='xx'
self.session='oo'
ctx=RequestContext()
_request_ctx_stack.push(ctx)
'''
__storage__={
12312:{stack:[ctx(session/request),]}
}
''' # _lookup_req_object 帮我们取出以stack为键的值
def _lookup_req_object(arg):
ctx=_request_ctx_stack.top()
return getattr(ctx,arg) request=functools.partial(_lookup_req_object,'request')
session=functools.partial(_lookup_req_object,'session') print(request())
print(session())

源码中的LocalStack

Flask上下文管理源码

上下文request请求

请求到来时:
#将request、session放到ctx中
ctx=RequestContext(self,environ)
ctx.request=Request(environ)
ctx.session=None 将包含了request、session的ctx对象放到‘箱子’中
{
12312: {ctx:ctx对象}
12232: {ctx:ctx对象}
11232: {ctx:ctx对象}
13542: {ctx:ctx对象}
}
执行视图函数: from flask import request,session
#request.method不是执行了request中的method方法,而是代表执行一个线程
#12312:{ctx:ctx对象}中的 ctx对象的request方法,request中的method方法
request.method 请求结束:
根据当前线程的唯一标识,将‘箱子’上的数据移除

大框架

上下文管理 request
a.温大夜:wsgi
b.鞠腾 :
ctx=RequestContext(session,request)
ctx.push() c.马玲:
LocalStack,把ctx对象添加到Local中 d.空调:Local
__storage__={
12312:{stack:[stx,]}
}
#请求来了,执行wsgi(温大爷处报道),接着R

上下文管理 request框架

上下文管理 request

    a.温大夜:wsgi

        代码体现:

        开始flask程序
执行run.app()方法
---->执行__call__方法
---->执行wsgi_app()方法 b.鞠腾 :
ctx=RequestContext(session,request) #鞠腾将水装满杯子
ctx.push() b代码流程.
---->在wsgi_app方法中执行self.request_context(environ)
----->执行wsgi_app()方法,返回RequestContext(self, environ)
将session和request方法放到RequestContext中
----->接着执行ctx.push() c.马玲:
LocalStack,把ctx对象添加到Local中 #马玲把杯子放到空调上 c代码流程
--->push()方法中执行_request_ctx_stack.push(self) #self为ctx对象
--->由于_request_ctx_stack = LocalStack()
--->在LocalStack的__init__方法中self._local = Local()
---->Local()类中执行__init__方法中的__setattr__、__getattr__方法 d.空调:Local
__storage__={
12312:{stack:[ctx,]}
}

上下文管理 request框架解释-轻松版代码流程

图解上下文管理request请求

Flask上下文管理源码--亲自解析一下

flask-session

当用户请求发来时,flask-session流程和上边的上下文request流程差不多。唯一的不同,在最后多了一个步骤,(黑哥)从LocalStack处取到ctx中的空session,给session赋值(从浏览器的cookie处取到session,采用RedisSessionInterface中的open_session()找到session),然后通过save_session()将session保存在redis中。

from flask_session import RedisSessionInterface  #查看flask_session源码
from flask import Flask,session,request
import redis
from flask_session import Session app=Flask(__name__) # 将session存入redis中的配置操作,就这三行,源码在RedisSessionInterface中,将session保存到
# 原理:a.session保存到redis中 数据结构:session:随机字符串1:sdfsdasdfsd34sfas
# session:随机字符串2:sdfsdasdfsd34sfas
# b.使用uuid生成的随机字符串返回给用户
app.config['SESSION_TYPE']='redis'
app.config['SESSION_REDIS']=redis.Redis(host='140.143.227.206',port=6379,password='')
Session(app) @app.route('/login')
def login():
session['user']='alex'
return 'adsfsaa' @app.route('/index')
def index():
print(session.get('user'))
return '...' if __name__ == '__main__':
app.run()

flask-session