主要内容:
1 线程中的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, 则会造成数据混乱的问题.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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()
2 偏函数: 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
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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()
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程序运行的过程中,保存的一些配置信息,比如程序文件名,数据库的连接,用户信息等。