Flask上下文管理源码--亲自解析一下
前戏
偏函数
1 def index(a,b): 2 return a+b 3 4 # 原来的调用方法 5 # ret=index(1,2) 6 # print(ret) 7 8 # 偏函数--帮助开发者自动传递参数 9 import functools 10 new_func=functools.partial(index,666) 11 ret=new_func(1) 12 print(ret) #结果 667
执行父类方法
1 class Base(object): 2 def func(self): 3 print('Base.func') 4 5 class Foo(Base): 6 def func(self): 7 # 方式一:根据mro的顺序执行对应方法 8 # super().func() 9 # 方式二:主动执行Base方法 10 Base.func(self) 11 print('Foo.func') 12 13 obj=Foo() 14 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='12' 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.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()
# 为协程开辟一个隔离的内存空间 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}}
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) #12 # 结果: # -----set---- # ---get------ # 12
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())
1 try: 2 from greenlet import getcurrent as get_ident 3 except ImportError: 4 try: 5 from thread import get_ident 6 except ImportError: 7 from _thread import get_ident 8 9 import functools 10 class Local(object): 11 __slots__ = ('__storage__', '__ident_func__') 12 13 def __init__(self): 14 object.__setattr__(self, '__storage__', {}) 15 object.__setattr__(self, '__ident_func__', get_ident) # self.__ident_func__() 相当于slef.get_ident() 16 17 def __getattr__(self, name): 18 try: 19 print('---执行getattr------') 20 return self.__storage__[self.__ident_func__()][name] 21 22 except KeyError: 23 raise AttributeError(name) 24 25 def __setattr__(self, name, value): 26 ident = self.__ident_func__() 27 storage = self.__storage__ 28 print("-----执行setattr----") 29 try: 30 # ident=22344 name=age value=12 即:{22344:{'alex':12}} 31 storage[ident][name] = value 32 except KeyError: 33 storage[ident] = {name: value} 34 35 def __delattr__(self, name): 36 try: 37 del self.__storage__[self.__ident_func__()][name] 38 except KeyError: 39 raise AttributeError(name) 40 41 42 # 使用local为线程开辟了一个内存空间后,往内存空间中放数据时,可以使用append,但是比较麻烦,源码帮我们提供了方法,即LocalStack这个类 43 # LocalStack帮我们将列表维护成一个栈 44 # 往线程的栈中存放数据--自己写 45 ''' 46 __storage__={ 47 123121:{ ''stack:[] } 48 } 49 ''' 50 # obj = Local() 51 # obj.stack = [] 52 # obj.stack.append('老杜') 53 # obj.stack.append('小杜') 54 # print(obj.stack) 55 # print(obj.stack.pop()) 56 # print(obj.stack) 57 58 # 往线程的栈中存放数据--源码提供的 59 class LocalStack(object): 60 def __init__(self): 61 self._local=Local() 62 63 def push(self, obj): 64 rv = getattr(self._local, 'stack', None) 65 if rv is None: 66 self._local.stack = rv = [] 67 rv.append(obj) 68 return rv 69 70 def pop(self): 71 stack = getattr(self._local, 'stack', None) 72 if stack is None: 73 return None 74 elif len(stack) == 1: 75 return stack[-1] 76 else: 77 return stack.pop() 78 79 def top(self): 80 try: 81 return self._local.stack[-1] 82 except (AttributeError, IndexError): 83 return None 84 85 _request_ctx_stack=LocalStack() 86 87 # RequestContext类帮我们往键为 stack的字典存值 88 class RequestContext(object): 89 def __init__(self): 90 self.request='xx' 91 self.session='oo' 92 ctx=RequestContext() 93 _request_ctx_stack.push(ctx) 94 ''' 95 __storage__={ 96 12312:{stack:[ctx(session/request),]} 97 } 98 ''' 99 100 # _lookup_req_object 帮我们取出以stack为键的值 101 def _lookup_req_object(arg): 102 ctx=_request_ctx_stack.top() 103 return getattr(ctx,arg) 104 105 request=functools.partial(_lookup_req_object,'request') 106 session=functools.partial(_lookup_req_object,'session') 107 108 print(request()) 109 print(session())
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 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请求
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='123456') 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()