117 flask中的上下文实现原理 偏函数 线程 setattr

主要内容:

线程中的local:    from  threading import local                  https://blog.csdn.net/woshiluoye9/article/details/72544764

  a : 使用local的原因: 多个请求过来不会冲突

  b : 在python中获取ThreadLocal最简单的方法是使用threading.local()

  c : thredlocal定义: threadlocal 是线程的局部变量, 每一个线程独有的, 其他线程不能进行访问, 通常是类中的私有静态字段, 是对字段值的一个copy, 他们希望将状态与某一个线程相关联, 占用内存, 节省时间.当使用ThreadLocal维护变量的时候 为每一个使用该变量的线程提供一个独立的变量副本,即每个线程内部都会有一个该变量,这样同时多个线程访问该变量并不会彼此相互影响,因此他们使用的都是自己从内存中拷贝过来的变量的副本, 这样就不存在线程安全问题,也不会影响程序的执行性能。

import threading
import time
from threading import local
class Foo(object):
    pass
foo = local()
th_local = {
    6316:{foo.num:0},
    6318:{foo.num:1},
    6319:{foo.num:2},
}
def add(i):
    foo.num = i
    time.sleep(1)
    # 打印当前的线程号
    print(foo.num, i, threading.current_thread().ident)
for i in range(20):
    th = threading.Thread(target=add, args=(i, ))
    th.start()

  如果不适用local, 则会造成数据混乱的问题.

import threading
import time
from threading import local
class Foo(object):
    pass
foo = Foo()
def add(i):
    foo.num = i
    time.sleep(1)
    print(foo.num, i, threading.current_thread().ident)

for i in range(20):
    th = threading.Thread(target=add, args=(i, ))
    th.start()
View Code

偏函数:   from functools  import partial

from functools import partial
def ab(a, b):
    print(a)
    return a + b
new_ab = partial(ab, "request")
print(new_ab("200 ok!"))

def abc(a, b, c):
    print(a, b)
    return a+b+c
new_abc = partial(abc, 1, 3)
print(new_abc(2))

  偏函数, 使用上面的new_ab函数相当于原函数固定了a的值, 即新的函数只需要接收一个参数.

3   flask --werkzeug 请求响应源码分析:  https://blog.csdn.net/u013210620/article/details/80051753#%E7%A4%BA%E4%BE%8B

from werkzeug.wrappers import Response, Request
from werkzeug.serving import run_simple
@Request.application
def app(req):
    print(req, type(req))
    print(req.method)
    print(req.path)
    print(req.args)
    return Response("200 ok!")

run_simple(hostname="127.0.0.1", port=7890, application=app)
app()
View Code

4   flask中的上下文及实现原

 a : 上下文的定义

    相当于一个容器, 保存了flask程序运行过程中的一些信息.在计算机中, 相对于进程而言, 上下文就是进程执行时的环境, 具体就是flask中有两种上下文:请求上下文和应用上下文.

   b : 请求上下文的定义

   request和session都属于请求上下文对象. request是封装http请求的内容,针对http请求, session用来记录请求会话中的信息, 针对的是用户信息.

 c : 请求上下文的实现原理

   Flask是一个基于WerkZeug实现的框架,因此Flask的App Context和Request Context是基于WerkZeug的Local Stack的实现。 
这两种上下文对象类定义在flask.ctx中,ctx.push会将当前的上下文对象压栈压入flask._request_ctx_stack中,这个_request_ctx_stack同样也是个Thread Local对象,也就是在每个线程中都不一样,上下文压入栈后,再次请求的时候都是通过_request_ctx_stack.top在栈的顶端取,所取到的永远是属于自己线程的对象,这样不同线程之间的上下文就做到了隔离。请求结束后,线程退出,ThreadLocal本地变量也随即销毁,然后调用ctx.pop()弹出上下文对象并回收内存。

    d :  请求上下文的源码流程

    存储 :  1) 当执行app实例的时候调用app.__call__方法, 返回值:return self.wsgi_app(environ, start_response)  environ是原始的请求信息, start_response是返回的对象.

        2) 执行wsgi_app,     ctx = self.request_context(environ)   --->  request_context --> return RequestContext(self, environ): 进入到RequestContext类中,执行__init__犯法, 得到结果, ctx = [request, session]

    def __init__(self, app, environ, request=None):
        # app是falsk实例
        self.app = app
        if request is None:
            # 序列化data, file, form, json里的数据
            request = app.request_class(environ)
        self.request = request
        self.url_adapter = app.create_url_adapter(self.request)
        self.flashes = None
        self.session = None

       3)  执行wsgi_app中ctx.push() --> RequestContext中的def push(self)方法:  self是ctx

       top = _request_ctx_stack.top :  首先看_request_ctx_stack是LocalStack实例--> 执行该类的__init__方法.--> self._local = Local() --> 进入local类中的__init__方法:  得到local的实例对象:  {'__storage__':{}, '__ident_func__':get_ident}, 即_request_ctx_stack得到的结果是local的实例化对象.执行.top方法, 由于没有stack, 返回值是none

       接着执行_request_ctx_stack.push(self)  --> push方法 --> 

    def push(self, obj):
        # obj = ctx = [request, session]
        rv = getattr(self._local, 'stack', None)
        if rv is None:
            # 执行__setarr方法
            self._local.stack = rv = []
        rv.append(obj)
        return rv

      self._local.stack = rv = [] -- > 触发local类中的__setattr__方法,

    def __setattr__(self, name, value):
        # 获得线程号
        # # local --> {'__storage__':{"进程号":{stack:[request, session]}}, '__ident_func__':get_ident}
        ident = self.__ident_func__()
        storage = self.__storage__
        try:
            storage[ident][name] = value
        except KeyError:
            storage[ident] = {name: value}

      执行完push得到结果 : local --> {'__storage__':{"进程号":{stack:[request, session]}}, '__ident_func__':get_ident}

        调用:  视图函数的处理

     1)   import  request  :  进入request中:  request = LocalProxy(partial(_lookup_req_object, 'request'))

     2)   先执行里面的偏函数:  执行_lookup_req_object

def _lookup_req_object(name):   name 为request或者是session
    top = _request_ctx_stack.top
    if top is None:
        raise RuntimeError(_request_ctx_err_msg)
从  {'__storage__':{"进程号":{stack:[request, session]}}, '__ident_func__':get_ident}对象反射出request或者是session return getattr(top, name)

    3)  当我们使用request.method的时候, 调用LocalProxy, 执行__init__方法, 

 def __init__(self, local, name=None):
        # local = 就是偏函数
        object.__setattr__(self, '_LocalProxy__local', local)
        object.__setattr__(self, '__name__', name)
        if callable(local) and not hasattr(local, '__release_local__'):
            object.__setattr__(self, '__wrapped__', local)

      当使用.方法获取值的时候调用的__getattr__方法, 取出method里的数据

    def __getattr__(self, name):
        #  method form
        if name == '__members__':
            return dir(self._get_current_object())
        return getattr(self._get_current_object(), name)

      接着执行_get_current_object方法, _local实际上就是我们的偏函数_lookup_req_object

    def _get_current_object(self):
        if not hasattr(self.__local, '__release_local__'):
            return self.__local()

  e : 应用上下文:  https://blog.csdn.net/Enjolras_fuu/article/details/79892961

  f :  请求上下文和应用上下文的区别:

请求上下文:保存了客户端和服务器交互的数据。 
应用上下文:在flask程序运行的过程中,保存的一些配置信息,比如程序文件名,数据库的连接,用户信息等。

  

原文地址:https://www.cnblogs.com/gyh412724/p/10145873.html