zoukankan      html  css  js  c++  java
  • 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
    View Code

    执行父类方法

     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()
    View Code

    面向对象中特殊方法

    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__中
    View Code

     用列表实现一个栈

    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())  #打印第一个进去的元素
    # 打印结果:后打印我,是一个先进后出的
    View Code

    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字段
    View Code

    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)  #12
    
    # 结果:
    # -----set----
    # ---get------
    # 12
    源码中的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
      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())
    源码中的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-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()
    flask-session
  • 相关阅读:
    用自己电脑搭建外网可访问的服务器(转)
    vue页面开发,简单技术点总结
    学习网站
    bzoj4530&&#3805. 大融合
    bzoj4137&&dtoj#2259. 火星商店问题
    bzoj-4009&&dtoj#2284. 接水果(fruit)
    bzoj5407: girls
    bzoj3498: PA2009 Cakes
    CF938F Erasing Substrings
    dtoj#4138. 染色(ranse)
  • 原文地址:https://www.cnblogs.com/djfboai/p/10531825.html
Copyright © 2011-2022 走看看