上下文

 

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