主要内容:
1 线程中的local: from threading import local
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
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 请求响应源码分析:
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 : 应用上下文:
f : 请求上下文和应用上下文的区别:
请求上下文:保存了客户端和服务器交互的数据。
应用上下文:在flask程序运行的过程中,保存的一些配置信息,比如程序文件名,数据库的连接,用户信息等。