• 上下文


     

     local推导步骤

    #多个线程修改同一个数据,复制多份变量给每个线程用,为每个线程开辟一块空间进行数据存储

    这里的local实现原理,通过get_ident获取线程ID,通过getcurrent获取协程ID,放到字典中,
    #不用local
    from threading import Thread
    import time
    lqz = -1
    
    def task(arg):
        global lqz
        lqz = arg
        time.sleep(2)              #最后显示9个相同的数字
        print(lqz)
    
    for i in range(10):
        t = Thread(target=task,args=(i,))
        t.start()
    
    ---------------------------------
    # 使用local对象
    from threading import Thread
    from threading import local
    import time
    
    # {'线程id1':{'args':1},'线程id2':{'args':2}}
    #local存的格式如上,根据线程id进行存值
    lqz = local()
    
    def task(arg):
        lqz.arg = arg
        time.sleep(2)
        print(lqz.arg)
    
    for i in range(10):
        t = Thread(target=task,args=(i,))
        t.start()
    
    
    --------------------------------------------
    #自定义local,函数版
        # {'线程id1': {'args': 1}, '线程id2': {'args': 2}, '线程id3': {'args': 3}}
    
    from threading import get_ident,Thread
    import time
    storage = {}
    def set(k,v):
        #get_ident的作用是获取线程的id号
        ident = get_ident()
    
        if ident in storage:            
            storage[ident][k] = v
        else:
            storage[ident] = {k:v}
    
    def get(k):
        ident = get_ident()
        return storage[ident][k]
    
    def task(arg):
        set('val',arg)      #设置值
        v = get('val')       #取值
        print(v)
    
    for i in range(10):
        t = Thread(target=task,args=(i,))
        t.start()
    
    
    
    
    ----------------------------------------------
    
    #面向对象版的local
    
    #{'线程id1': {'args': 1}, '线程id2': {'args': 2}, '线程id3': {'args': 3}}
    from threading import get_ident,Thread
    import time
    
    class Local(object):
        storage = {}
    
        def set(self, k, v):
            ident = get_ident()
            if ident in Local.storage:        #类的属性可以点出来
                Local.storage[ident][k] = v
            else:
                Local.storage[ident] = {k: v}
    
        def get(self, k):
            ident = get_ident()
            return Local.storage[ident][k]
    obj = Local()
    def task(arg):
        obj.set('val',arg)
        v = obj.get('val')
        time.sleep(1)
        print(v)
    for i in range(10):
        t = Thread(target=task,args=(i,))
        t.start()
    
    
    
    
    ----------------------------------------------
    #改进版本
    from threading import get_ident,Thread
    import time
    class Local(object):
        storage = {}
        def __setattr__(self, k, v):
            ident = get_ident()
            if ident in Local.storage:
                Local.storage[ident][k] = v
            else:
                Local.storage[ident] = {k: v}
        def __getattr__(self, k):
            ident = get_ident()
            return Local.storage[ident][k]
    obj = Local()
    # obj2=Local()
    # obj3=Local()
    # obj4=Local()
    # obj5=Local()
    def task(arg):
        obj.val = arg
        time.sleep(1)
        print(obj.val)
    for i in range(10):
        t = Thread(target=task,args=(i,))
        t.start()
    
    
    
    --------------------------------------------
    #每次生成local对象用的都是自己的字典,上面那个用的全是类的字典,不管生成几个对象,用的都是同一个
    from threading import get_ident,Thread
    import time
    class Local(object):
        def __init__(self):
            object.__setattr__(self,'storage',{})
      
            # self.storage={},, #这样写会出现递归,所以用是上边的写法
        def __setattr__(self, k, v):
            ident = get_ident()
            if ident in self.storage:
                self.storage[ident][k] = v
            else:
                self.storage[ident] = {k: v}
        def __getattr__(self, k):
            ident = get_ident()
            return self.storage[ident][k]
    obj = Local()
    def task(arg):
        obj.val = arg
        # obj.xxx = arg
        time.sleep(1)
        print(obj.val)
    for i in range(10):
        t = Thread(target=task,args=(i,))
        t.start()
    #到此为止,自己写了一个local对象,只支持线程
    
    
    ---------------------------------------------------
    #要支持协程
    try:
        #getcurrent 获取协程id
        from greenlet import getcurrent as get_ident
    except Exception as e:
        from threading import get_ident
    from threading import Thread
    import time
    class Local(object):
        def __init__(self):
            object.__setattr__(self,'storage',{})
        def __setattr__(self, k, v):
            ident = get_ident()
            if ident in self.storage:
                self.storage[ident][k] = v
            else:
                self.storage[ident] = {k: v}
        def __getattr__(self, k):
            ident = get_ident()
            return self.storage[ident][k]
    obj = Local()
    def task(arg):
        obj.val = arg
        obj.xxx = arg
        print(obj.val)
    for i in range(10):
        t = Thread(target=task,args=(i,))
        t.start()
    推导过程
    #请求来了执行__call__
    
        def __call__(self, environ, start_response):
            """The WSGI server calls the Flask application object as the
            WSGI application. This calls :meth:`wsgi_app` which can be
            wrapped to applying middleware."""
            return self.wsgi_app(environ, start_response)     #点进来
    
    
    这个方式基于哪个类?  
    往上层推,基于自己写的app = Flask(__name__)   这个类
        def wsgi_app(self, environ, start_response): #flask所有请求上下文基于这几行 
            ctx = self.request_context(environ)   #与ctx.push()一起将请求相关的数据environ封装到了RequestContext对象中,再将对象封装到local中(每个线程/协程独立空间存储)   
            error = None
            try:
                try:
                    ctx.push()     
                    response = self.full_dispatch_request()      #下面这些就是执行当前函数
                except Exception as e:
                    error = e
                    response = self.handle_exception(e)
                except:  # noqa: B001
                    error = sys.exc_info()[1]
                    raise
                return response(environ, start_response)
            finally:
                if self.should_ignore_error(error):
                    error = None
                ctx.auto_pop(error)       #把当前请求对象从取出来
           ctx = self.request_context(environ)      #environ请求相关所有东西
           #看这一行,self是当前请求对象,也就是实例化flask的app
    
    点request_context进来
        def request_context(self, environ):       
            return RequestContext(self, environ)   #返回这个类
                        #ctx = RequestContext(self, environ)    相当于ctx等于这个类
    
    
    class RequestContext(object):
         #self应该是ctx吧
        def __init__(self, app, environ, request=None, session=None):
            self.app = app
            if request is None:   #第一次来肯定是None,看类中传的参数
                request = app.request_class(environ)    #发现这又是一个类,这一步把 
                                                                                 #request重新封装了吧
    
            self.request = request
            self.url_adapter = None
            try:
                self.url_adapter = app.create_url_adapter(self.request)
            except HTTPException as e:
                self.request.routing_exception = e
            self.flashes = None
            self.session = session
    
    
    
    
    
    
    -------------------------------------------------------
    
    
        def request_context(self, environ):
            """Create a :class:`~flask.ctx.RequestContext` representing a
            WSGI environment. Use a ``with`` block to push the context,
            which will make :data:`request` point at this request.
    
            See :doc:`/reqcontext`.
    
            Typically you should not call this from your own code. A request
            context is automatically pushed by the :meth:`wsgi_app` when
            handling a request. Use :meth:`test_request_context` to create
            an environment and context instead of this method.
    
            :param environ: a WSGI environment
            """
    创建一个:类:‘~ flask.ctx。RequestContext的代表
    WSGI环境。使用“with”块来推送上下文,
    这将使:data: ' request '指向这个请求。
    看到:医生:“/ reqcontext”。
    通常,您不应该从自己的代码中调用它。一个请求
    上下文被:meth: ' wsgi_app '自动推送
    处理一个请求。使用:meth: ' test_request_context '来创建
    环境和上下文,而不是这个方法。
    :param environment on:一个WSGI环境
            """
    
    
            return RequestContext(self, environ)
    #看第二行
    
            ctx.push()
            #上一行知道ctx=RequestContext(self, environ),所以执行的是这个类的push方法
    
    
    
        def push(self):      #self是ctx
         #进来之后先看最后一句
    
            top = _request_ctx_stack.top
            if top is not None and top.preserved:
                top.pop(top._preserved_exc)
            app_ctx = _app_ctx_stack.top
            if app_ctx is None or app_ctx.app != self.app:
                app_ctx = self.app.app_context()
                app_ctx.push()
                self._implicit_app_ctx_stack.append(app_ctx)
            else:
                self._implicit_app_ctx_stack.append(None)
    
            if hasattr(sys, "exc_clear"):
                sys.exc_clear()
    
            _request_ctx_stack.push(self)  
                 #这个push方法是_request_ctx_stack对象的类LocalStack的,不是当前类的    
                 #把self添加到_request_ctx_stack中
    
    #来到_request_ctx_stack
    
     #下面这些东西都是全局变量,程序开始阶段已经存在
    _request_ctx_stack = LocalStack() 
    _app_ctx_stack = LocalStack()
    current_app = LocalProxy(_find_app)
    request = LocalProxy(partial(_lookup_req_object, "request"))
    session = LocalProxy(partial(_lookup_req_object, "session"))
    g = LocalProxy(partial(_lookup_app_object, "g"))
    
    
    
    #看看LocalStack这个类
    class LocalStack(object):
        def __init__(self):      
    #self是_request_ctx_stack        #_request_ctx_stack = LocalStack() 
            self._local = Local()
    
    
    
    
    #看看Local这个类
    #程序刚启动这里的数据是空的,仅仅完成实例化,
    
    class Local(object):
        __slots__ = ("__storage__", "__ident_func__")
    
        def __init__(self):
            object.__setattr__(self, "__storage__", {})
            object.__setattr__(self, "__ident_func__", get_ident)
    
        def __getattr__(self, name):
            try:
                return self.__storage__[self.__ident_func__()][name]
            except KeyError:
                raise AttributeError(name)
    
        def __setattr__(self, name, value):
            ident = self.__ident_func__()
            storage = self.__storage__
            try:
                storage[ident][name] = value
            except KeyError:
                storage[ident] = {name: value}
    ------------------------------------
    #看完下一个方法在回头看这里
    #相当于
    name=stack
    value=[]
    
    {
        唯一ID:{stack:[ctx]}    
    }
    
    
    
    
    
    
    
    #然后看LocalStack的push方法
        def push(self, obj):
            rv = getattr(self._local, "stack", None)     #刚来到时候是None
            if rv is None:
                self._local.stack = rv = []
                #self._local,执行__setattr__      #现在看上边
            rv.append(obj)      #这一步把当前请求对象添加到列表中,也就是ctx
            return rv
    
    
        
    #此时在自己写的函数中的request,点进去
    _request_ctx_stack = LocalStack()
    _app_ctx_stack = LocalStack()
    current_app = LocalProxy(_find_app)
    #request = LocalProxy(partial(_lookup_req_object, "request"))   还是来到全局变量
    session = LocalProxy(partial(_lookup_req_object, "session"))
    g = LocalProxy(partial(_lookup_app_object, "g"))
    
    
    #来到LocalProxy
    class LocalProxy(object):
        里边有很多方法,此时自己写的类中的request相应的操作,会执行当前类中相对应的方法,比如,print(request)  执行_str__方法
            #另外执行这些方法时,都执行偏函数LocalProxy(partial(_lookup_req_object, "request"))
    
    
    #来到_lookup_req_object
    def _lookup_req_object(name):
        top = _request_ctx_stack.top      
        #_request_ctx_stack也是全局变量
        #去localLocalStack找top
        if top is None:
            raise RuntimeError(_request_ctx_err_msg)
        return getattr(top, name)
    
    
    #来到top,
        def top(self)
            try:
                return self._local.stack[-1]     

    请求上下文执行流程

    #0 flask项目一启动,有6个全局变量
    #globals.py中
        _request_ctx_stack:LocalStack对象
        _app_ctx_stack :LocalStack对象
        request : LocalProxy对象
        session : LocalProxy对象
    
    #1请求来的时候。先到app.__call__()start_response)
    #app.py
    
    class Flask(_PackageBoundObject):
        def __call__(self, environ, start_response):
            return self.wsgi_app(environ, start_response)    #执行
    
    #2、点wsgi_app()进去
    #2.1执行  ctx = self.request_context(environ)
        def wsgi_app(self, environ, start_response):
    
            #ctx是RequestContext的对象,里面包含了当次请求的request,session
            ctx = self.request_context(environ)        #点request_context
            error = None
            try:
                try:
                    ctx.push()   #此时的push其实是RequestContext类的push方法
    2.1.1来到
        def request_context(self, environ):
            return RequestContext(self, environ)        
                #把RequestContext返回给了ctx  ,所以ctx是RequestContext的对象
    
    
    #2.2点 RequestContext进去
    #ctx.py   ,先看push方法
    
    class RequestContext(object):
            def push(self):                             
                        _request_ctx_stack.push(self)   
        # 前面知道_request_ctx_stack是全局变量
        #知道ctx是RequestContext类的对象,所以此时self是ctx,
    
    #2.2.1点_request_ctx_stack进去
    #globals.py
    _request_ctx_stack = LocalStack()      #进去找push方法
    
    
    #2.2.2
    #local.py
    class LocalStack(object):
        def push(self, obj):    #传进来的obj就是ctx
            rv = getattr(self._local, "stack", None)
             #self._local是init的时候,实例化进来的,也就是flask定义的local()对象可以点进去看看self._local = Local(),local和上面推导的哟杨
             #一开始没有stack
            if rv is None:
                self._local.stack = rv = []
            rv.append(obj)
            return rv
             #此时{"线程id:{'static':[ctx]},"线程id2:{'static':[ctx]}}
             #也就是把ctx放到local对象里
    #那么,往回看,
    _request_ctx_stack = LocalStack()  #相当于这句话完成把ctx放到local里
    #再往回看
    ctx.push()      #也就是这句话完成的
    请求的过程

    print(request)

    #其实走的是LocalProxy类的__ste__
    request = LocalProxy(partial(_lookup_req_object, "request"))
    
    #点进去找__repr__,先把他理解为__str__
        def __repr__(self):
            try:
                obj = self._get_current_object()    #点进去
            if not hasattr(self.__local, "__release_local__"):
                return self.__local()       #是从__init__传过来的,隐藏属性传的
        #self.__local就是传入的偏函数,偏函数的执行结果是request对象
    
        def __init__(self, local, name=None):
            object.__setattr__(self, "_LocalProxy__local", local)
            object.__setattr__(self, "__name__", name)
    
    # return self.__local() ,此时i相当于偏函数partial(_lookup_req_object, "request")加括号执行,那么点_lookup_req_object进去
    def _lookup_req_object(name):        #name     ‘request’
        top = _request_ctx_stack.top       #top就是ctx的对象
        if top is None:
            raise RuntimeError(_request_ctx_err_msg)
        return getattr(top, name)      #返回的就是request对象
    
    也就是打印的request
    
    --------------------------------
    #那么    print(request.method)   用点属性的时候,又是怎么执行的呢
    通过__grtattr__方法实现
    
        def __getattr__(self, name):
            if name == "__members__":
                return dir(self._get_current_object())
            return getattr(self._get_current_object(), name)  #通过反射找出点的内容
    View Code

     

     flask有一个请求上下文,还有一个应用上下文

    #ctx:
        -是:RequestContext对象:封装了request和session
        -调用了:_request_ctx_stack.push(self)就是把:ctx放到了那个位置
    #app_ctx:
        -是:AppContext(self) 对象:封装了当前的app和g
        -调用 _app_ctx_stack.push(self) 就是把:app_ctx放到了那个位置

    g对象

    #g对象是什么
    专门用来存储用户信息的g对象,g的全称的为global 
    g对象在一次请求中的所有的代码的地方,都是可以使用的 
    
    #g对象和session的区别
    session对象是可以跨request的,只要session还未失效,不同的request的请求会获取到同一个session,但是g对象不是,g对象不需要管过期时间,请求一次就g对象就改变了一次,或者重新赋值了一次

     

    from flask import Flask,g,request
    app = Flask(__name__)
    
    
    @app.before_request   #好像必须与它一起用
    def xxxx():
        g.lqz='lqz'
        # request.lqz='lqz' 效果与g一样,估计作者是怕误操作 request.method='lqz'
    
    
    @app.route('/',methods=['GET','POST'])
    def index():
        print(g.lqz)
    
        return 'dddddd'
    if __name__ == '__main__':
        app.run(port=8888)
    g简单使用

     

     

     

     

     

     

     

     

     

     

     

     

     

    偏函数

    #偏函数
    #提前给函数传值,以后就可以少传值了
    from functools import partial
    def add(a,b,c,d):
        return a+b+c+d
    # print(add(1,2,3,4))
    add=partial(add,1,2)
    print(add(3,4))
    
    >>10
    View Code
  • 相关阅读:
    如何设置路由器实现静态IP配置
    linux内核源码结构
    linux源码“.config”文件分析
    用python来调试网络程序
    dbm速算
    用python虚拟串口
    sed学习笔记
    贴一段shell代码
    ethtool使用记录
    mint锁屏设置
  • 原文地址:https://www.cnblogs.com/pdun/p/11207423.html
Copyright © 2020-2023  润新知