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
原文地址:https://www.cnblogs.com/djfboai/p/10531825.html