1. werkzurg
from werkzeug.serving import run_simple
def run(environ,start_response):
reuturn [b'hello world']
if __name__ == "__main__":
run_simple('localhost',4000,run) # run_simple --> 启动监听接收 socket(一个死循环); run 会加上 () 去执行
2. 所有请求的入口
def __call__(self, environ, start_response): # 当请求来的时候,才会执行 __call__ 方法 """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) # wsgi_app() 真正去执行源码 # app.run() 这行代码执行的时候,并没有执行 __call__ 方法
3. Local() ---> 把不同线程的数据隔离
import time import threading # 获取 线程或者协程的唯一id try: import greenlet get_ident = greenlet.getcurrent except Exception: get_ident = threading.get_ident class Local(object): DIC = {} # 数据都放到这个大字典中 def __getattr__(self,item): get_ident = get_ident() if get_ident in self.DIC: return self.DIC[get_ident].get(item) return None def __setattr__(self,key,value): get_ident = get_ident() if get_ident in self.DIC: self.DIC[get_ident][key] = value else: self.DIC[get_ident] = {key:value} # __getattr__ 和 __setattr__ 的用处 ; 同一个对象的同一个属性,根据线程和协程号这个唯一标识,来获取不同的值
4. 上下文管理(第一次)
请求到来时: # ctx = RequestContext(self,environ) # self 是 app对象,environ表示请求相关的原始数据 # ctx.request = Request(environ) # ctx.session = None # 将包含了 request和session 的ctx对象打包放到某个地方(相当于一个大字典;根据线程或协程加个唯一标识,放进去的,所以数据相互隔离) { 1232:{ctx:ctx对象}, ... } 视图函数: from flask import request,session # 上述代码背后的过程:根据当前线程或者协程的唯一标识,取到 ctx对象,然后取到 request和session 请求结束: 根据当前线程的唯一标识,将 大字典 中该线程对应的数据移除
5. 偏函数
import functools def func(a,b): return a+b new_func = functools.partial(func,6) # 第一个参数是函数名;第二个参数 6 会当作 func 的第一个参数自动传入 func 中 ret = new_func(2) # new_func(2) 执行时, 就是 func 函数在执行,6作为第一个参数自动传入 func(),2作为第二个参数传入func print(ret) # 偏函数 ---> 帮助开发人员自动传递参数
6. 基于列表维护一个栈:
class Stack(object): """ 基于列表维护一个栈 """ def __init__(self): self.stack = [] def push(self,item): self.stack.append(item) def pop(self): return self.stack.pop() def top(self): """ 读取最后一个值 """ return self.stack[-1]
7. flask 中的 Local 类 和 LocalStack 类
# 自己写的 Local: try: from greenlet import getcurrent as get_ident except Exception as e: from threading import get_ident class Local(object): """docstring for Local""" def __init__(self, arg): object.__setattr__(self,"storage",{}) # Local 实例化的时候会在对象中存一个 "storage" 的空字典;注意这种设置对象属性值点语法的方法 # 这是不能使用 self.storage = {} ,因为 self.storage 会调用下面的 __setattr__ ,__setattr__ 中也有 self.storage def __setattr__(self,key,value): get_ident = get_ident() # 线程号 if get_ident not in self.storage: self.storage[get_ident] = {key,value} else: self.storage[get_ident][key] = value def __getattr__(self,item): get_ident = get_ident() if get_ident in self.storage: return self.storage[get_ident].get(item) return None # 实现的效果: 一个对象, .同一个属性的时候 可获取到不同的值(根据当前线程号等唯一标识)---> 为每个线程开辟一块独立的空间 # flask 中的 Local 和 LocalStack 类: # flask 源码中的 Local 部分源码: # 查询“路径”: flask 中的 globals ---> LocalStack ---> Local try: from greenlet import getcurrent as get_ident except ImportError: try: from thread import get_ident except ImportError: from _thread import get_ident class Local(object): # 类中的 __slots__() 的作用:只允许该类的实例添加 __slots__ () 中的属性 __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} def __delattr__(self, name): try: del self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name) obj = Local() obj.stack = [] """ 此时 obj. 的时候, 并不是给 obj 对象新添加了一个属性,而是把 "stack" 作为 key,[] 作为 value 放到了 __storage__ 这个大字典中,如下: { "线程号":{"stack":[]} } """ obj.stack.append("abc") obj.stack.append("123") obj.stack.pop() # 通过重写 __setattr__ 方法,Local对象所有通过 . 方法设置属性值时,都把 属性名和其对应的值放到了 __storage__ 这个大字典当前线程号对应的字典中; # 通过重写 __getattr__ 方法,Local对象通过 . 方法获取属性值时,也是从 __storage__ 这个大字典中 当前线程号对应的字典中 获取该属性key 对应的值 """ LocalStack 存的数据格式: __storage__ = { 1231:{"stack":[]}, ... } """ # 每次 obj.stack 这个栈 append 或者 pop 时都要加上 .stack ,下面做简化: 用一个代理 LocalStack class LocalStack(object): def __init__(self): self._local = Local() # 实例化 Local ---> 得到一个 __storage__ 属性对应的空字典 : Local()对象.__storage__ = {} def push(self,item): """Pushes a new item to the stack""" rv = getattr(self._local, 'stack', None) # self._local.stack ---> 触发 Local 的 __getattr__ 方法 if rv is None: self._local.stack = rv = [] # self._local.stack 和 rv 同时指向同一个引用; 此处执行 __setattr__ 方法 rv.append(item) return rv def pop(self): """Removes the topmost item from the stack, will return the old value or `None` if the stack was already empty. """ stack = getattr(self._local, 'stack', None) if stack is None: return None elif len(stack) == 1: return stack[-1] # 读取栈中的最后一个值 else: return stack.pop() ls = LocalStack() ls.push("abc") ls.push("123") ls.pop() """ __storage__ = { "线程号":{"stack":[]} } """ # LocalStack 对象相当于一个代理,帮助我们在Local中维护一个列表,并把这个列表维护成一个栈 """ 小结: Local 作用: 为每个线程开辟一个独立的空间 (内部维护了一个 __storage__ 的大字典,该字典中的key是当前线程号,字典的value是该线程独立的数据) LocalStack 作用: 帮助我们把 Local 中的一个列表维护成一个栈 """
8. 以上综合应用
# LocalStack的对象 ls push() 时不再传字符串,而是传一个字典,如: ls.push({"request":"xxx","session":"yyy"}) # 全局变量只有在程序启动时会加载执行
上下文管理:
1. request 上下文管理 (请求上下文管理)
app.__call__ ---> app.wsgi_app wsgi_app 中 : 1. ctx = self.request_context(environ) ---> 封装请求数据; environ --> 原始的请求数据 ctx = RequestContext(self, environ) ctx.request = Request(environ) ctx.session = None 2. ctx.push() ---> _request_ctx_stack.push(self) 1. # self 即是 ctx 对象, ctx 又封装了 request 和 session # self = ctx = (request,session) 2. _request_ctx_stack = LocalStack() ---> 这行代码执行完后(主要是 LocalStack()), 会在内存中创建一个名字为 __storage__ 的空字典 ---> _request_ctx_stack 帮助我们维护一个 __storage__ = {} ---> ctx.push() 执行完后 ---> __storage__ = { "当前线程号":{"stack":[封装了 request 和 session 的ctx对象]} } wsgi(帮助我们初步处理请求) ---> 然后执行 __call__ 方法 ---> __call__ 方法 执行 wsgi_app 方法 ---> wsgi_app 方法中: 1. 创建 ctx 对象 : ctx = RequestContext(self, environ) # environ 中有 request 和 session 2. 执行 push 方法 ctx.push() ---> LocalStack , 把 ctx对象添加到 Local 对象中, ---> Local 中的数据格式: __storage__ = { "当前线程号":{"stack":[封装了request和session的ctx对象]} } # 走到视图函数 取数据 : # 视图函数取数据也是通过 LocalStack 去 Local 中取数据(ctx对象),而不能直接去 Local 中取 原理: ctx = _request_ctx_stack.top ctx.request # 取 request ctx.session # 取 session @property def top(self): """The topmost item on the stack. If the stack is empty, `None` is returned. """ try: return self._local.stack[-1] except (AttributeError, IndexError): return None """ 第一阶段:请求到来 将 request 和 session 相关数据封装到 ctx=RequestContext() 对象中。 再通过 LocalStack 将 ctx 对象添加到 Local 中 __storage__ = { 1231:{"stack":[ctx(request,session)]} } 第二阶段: 视图函数中获取 request 或 session 方式一: 直接找 LocalStack 获取 _request_ctx_stack.top.request 方式二: 通过代理 LocalProxy 获取 from flask import request,session 上面两种方式都是通过 LocalStack 去 Local 中取值 """ # 通过代理 LocalProxy 获取 request 或 session request = LocalProxy(partial(_lookup_req_object, 'request')) ---> 获取 ctx 中的 request session = LocalProxy(partial(_lookup_req_object, 'session')) ---> 获取 ctx 中的 session # request 是一个 LocalProxy 的对象,以 request.method 为例, .mothod 会执行 LocalProxy 的 __getattr__ 方法: @implements_bool class LocalProxy(object): def __init__(self, local, name=None): object.__setattr__(self, '_LocalProxy__local', local) # __local 是 LocalProxy 在实例化的时候传入的参数 object.__setattr__(self, '__name__', name) if callable(local) and not hasattr(local, '__release_local__'): # "local" is a callable that is not an instance of Local or # LocalManager: mark it as a wrapped function. object.__setattr__(self, '__wrapped__', local) def __getattr__(self, name): if name == '__members__': return dir(self._get_current_object()) # 以 request.method 为例, 此时 name == "method" return getattr(self._get_current_object(), name) # 上面的 return 等同于下面的两句代码: """ obj = self._get_current_object() --> 从ctx中获取 request return getattr(obj,name) --> 从 request 中获取 method """ def _get_current_object(self): """Return the current object. This is useful if you want the real object behind the proxy at a time for performance reasons or because you want to pass the object into a different context. """ if not hasattr(self.__local, '__release_local__'): return self.__local() # 执行偏函数 # __local 是 LocalProxy 在实例化的时候传入的参数,即偏函数 partial(_lookup_req_object, 'request')--> 获取到ctx中的request try: return getattr(self.__local, self.__name__) except AttributeError: raise RuntimeError('no object bound to %s' % self.__name__) def _lookup_req_object(name): # name 为 "request" 或者 " session" top = _request_ctx_stack.top if top is None: raise RuntimeError(_request_ctx_err_msg) return getattr(top, name)