LocalProxy -- 代理实现request、session、g等
LocalProxy
LocalProxy用于代理Local对象和LocalStack对象 , 便于获取request、session、app、g等。
# 实例化了四个对象
current_app = LocalProxy(_find_app)
request = LocalProxy(partial(_lookup_req_object, "request"))
session = LocalProxy(partial(_lookup_req_object, "session"))
g = LocalProxy(partial(_lookup_app_object, "g"))
class LocalProxy(object):
def __init__(self, local, name=None):
object.__setattr__(self, "_LocalProxy__local", local)
使用到的知识点:
-
偏函数
能够自动传递参数
import functools def func(a1): print(a1) new_func = functools.partial(func,123) # 自动传递参数 new_func()
-
私有成员
class Foo: def __init__(self): self.name = 'alex' self.__age = 123 obj = Foo() print(obj.name) print(obj._Foo__age) # 通过_类名可以获取私有成员
-
getattr/setattr 的触发 -- .
class Foo(object): def __setattr__(self, key, value): print(key,value) def __getattr__(self, item): print(item) obj = Foo() obj.x = 123 # 执行setattr obj.x # 执行getattr
-
getitem/setitem的触发 -- []
class Foo(object): def __setitem__(self, key, value): print(key,value) def __getitem__(self, item): print(item) obj = Foo() obj['x'] = 123 # 执行setitem obj['x'] # 执行getitem
源码解析:
-
当在视图函数中使用
request.
时, 源码流程:request = LocalProxy(partial(_lookup_req_object, "request")) # 通过偏函数,将request参数传入
class LocalProxy(object): def __init__(self, local, name=None): object.__setattr__(self, "_LocalProxy__local", local) # _LocalProxy__local=local #local是: _lookup_req_object函数返回值,也是一个函数,只有 函数名 + ()就会执行
通过 LocalStack() 对象的top方法,获取栈顶的对象 ctx, 然后执行 ctx.request。 相当于
request=ctx.request
def _lookup_req_object(name): top = _request_ctx_stack.top if top is None: raise RuntimeError(_request_ctx_err_msg) return getattr(top, name) # ctx.request def top(self): try: return self._local.stack[-1] # 获取栈顶的对象 ctx except (AttributeError, IndexError): return None
当执行
request.method
时,会执行LocalProxy()对象的__getattr__
方法def __getattr__(self, name): return getattr(self._get_current_object(), name) # self._LocalProxy__local()执行,(私有成员),也就是ctx.request.method
-
session, 与request执行流程相似
session = LocalProxy(partial(_lookup_req_object, "session")) # 传入session
class LocalProxy(object): def __init__(self, local, name=None): object.__setattr__(self, "_LocalProxy__local", local) # _LocalProxy__local=local #local是: _lookup_req_object函数返回值,也是一个函数,只有 函数名 + ()就会执行
def _lookup_req_object(name): top = _request_ctx_stack.top if top is None: raise RuntimeError(_request_ctx_err_msg) return getattr(top, name) # 加 ()可执行
当设置 session[‘k1’] = 123时, 执行
__setitem__
def __setitem__(self, key, value): self._get_current_object()[key] = value
def _get_current_object(self): return self.__local() # self._LocalProxy__local()执行,(私有成员),也就是ctx.session['k1']=123
-
current_app,返回的是app
current_app = LocalProxy(_find_app)
通过 LocalStack() 对象的top方法,获取栈顶的对象app_ctx,
def _find_app(): top = _app_ctx_stack.top # 获取app_ctx对象 if top is None: raise RuntimeError(_app_ctx_err_msg) return top.app
-
g的流程相似,(略, 见下的使用)。
总结:
# session, request, current_app, g 全部都是LocalProxy对象。
"""
session['x'] = 123 ctx.session['x'] = 123
request.method ctx.request.method
current_app.config app_ctx.app.config
g.x1 app_ctx.g.x1
"""
g到底是个什么鬼?
在一次请求的周期中,可以在g中设置值,那么在本次以后的请求周期中都可以读取或赋值。
相当于是一次请求周期的全局变量。
应用:权限等
from flask import Flask,g
app = Flask(__name__,static_url_path='/xx')
@app.before_request
def f1():
g.x1 = 123
@app.route('/index')
def index():
print(g.x1)
return 'hello world'
if __name__ == '__main__':
app.run()