zoukankan      html  css  js  c++  java
  • 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程序运行的过程中,保存的一些配置信息,比如程序文件名,数据库的连接,用户信息等。
    

      

  • 相关阅读:
    一文快速入门分库分表(必修课)
    MySql分库分表与分区的区别和思考
    常用分库分表方案汇总
    分区分表分库
    MySQL分区和分表
    MySQL的聚集索引和非聚集索引
    PHP大文件上传支持断点上传组件
    PHP大文件上传支持断点上传工具
    Nginx大文件上传支持断点上传
    百度WebUploader大文件上传支持断点上传
  • 原文地址:https://www.cnblogs.com/gyh412724/p/10145873.html
Copyright © 2011-2022 走看看