背景
最近有面试一家公司,感觉准备的不是很充分,感觉很多东西都答的挺菜的,自己写的文章里面的问题都没答上来更是汗流浃背。 所以大概列了一下其中的问题,进行了一番总结补充,重新制定一下复习的计划。 其中里面我觉得有个问题我觉得挺有意思的,我记得大概是问了一下Flask的g有使用过?具体实现的原理是啥?
我想了一下 ,这不就是平时用来存储一下数据的全局变量的么?有啥原理的,后面大概翻了一下Flask的源码,具体从头捋了一遍,所以就有这篇文章。
源码阅读
首先我们从平时导入的"from flask import g"入手,定位到flask框架代码中g,作为起点继续往下探索;
_cv_app: ContextVar[AppContext] = ContextVar("flask.app_ctx")
app_ctx: AppContext = LocalProxy( # type: ignore[assignment]
_cv_app, unbound_message=_no_app_msg
)
current_app: Flask = LocalProxy( # type: ignore[assignment]
_cv_app, "app", unbound_message=_no_app_msg
)
g: _AppCtxGlobals = LocalProxy( # type: ignore[assignment]
_cv_app, "g", unbound_message=_no_app_msg
)
这里面重要的就两块地方,_cv_app,g:
- _cv_app这个对象实际上就是初始化一个上下文变量ContexVar,根据这个类型注解,这个上下文变量中的值是AppContext对象;
- g实质就是_AppCtxGlobals对象,但是这里是通过LocalProxy代理对象进行访问;
LocalProxy.__init__
接下来继续分析一下LocalProxy这个代理对象是怎么代理访问_AppCtxGlobals,也就是应用上下文中的对象:(代码过长,我截取初始化中最重要的几部分)
def __init__(
self,
local: ContextVar[T] | Local | LocalStack[T] | t.Callable[[], T],
name: str | None = None,
*,
unbound_message: str | None = None,
) -> None:
- 定义传入的参数,这里local就是_cv_app上下文变量,name就是"g";
if name is None:
get_name = _identity
else:
get_name = attrgetter(name)
- 生成一个可调用对象,用于提取对象中的属性值。后续get_name(obj)相当于直接调用obj.name属性;
elif isinstance(local, ContextVar):
def _get_current_object() -> T:
try:
obj = local.get()
except LookupError:
raise RuntimeError(unbound_message) from None
return get_name(obj)
这部分有两