回顧
在前一篇中,我們已經知道Flask是通過把用戶的請求信息放入到線程棧中進行存儲。例如你的線程id是9528,你進來後,生成一個stack,stack存放的就是9528的請求信息。你是9529、9531等依次對應,所有用戶的請求信息都是這樣存儲。
# {9528:{stack :[ctx(request,session)]}} # 存放的是線程id爲9528的請求信息
開始
接着前一篇繼續,在push中,我們知道了Flask的上下文存儲機制:
def push(self, obj): # obj= ctx = request_context=(request,session)
"""Pushes a new item to the stack"""
# 這個self往上找,self._local = Local=[{},get_ident]
rv = getattr(self._local, "stack", None)
if rv is None:
self._local.stack = rv = [] # {9528:{stack :[]}}
# _local對象.stack執行__setattr__方法
rv.append(obj)
# {9528:{stack :[ctx(request,session)]}}
return rv
往回翻到LocalStack
這裏:
# context locals
_request_ctx_stack = LocalStack() # {9528:{stack :[ctx(request,session)]}}
# _request_ctx_stack 請求上下文棧,實例化LocalStack(),執行__init__方法
# _request_ctx_stack._local = Local=[{},get_ident]
#最後變爲: [ {9528:{stack :[ctx(request,session)]}},get_ident]
知道了LocalStack
存儲的值是[ {9528:{stack :[ctx(request,session)]}},get_ident]
這種形式的。再往下看:
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app)
request = LocalProxy(partial(_lookup_req_object, "request")) #partial 偏函數
session = LocalProxy(partial(_lookup_req_object, "session"))
g = LocalProxy(partial(_lookup_app_object, "g"))
點擊進入到_lookup_req_object
中:
def _lookup_req_object(name): # name = request
top = _request_ctx_stack.top
# _request_ctx_stack._local = [ {9528:{stack :[ctx(request,session)]}},get_ident]
# 在實例內部別忘了("__storage__", "__ident_func__")與上面數組的小標是對應的
if top is None:
raise RuntimeError(_request_ctx_err_msg)
return getattr(top, name)
進入到top
中:
def top(self): # self._local = [ {9528:{stack :[ctx(request,session)]}},get_ident]
"""The topmost item on the stack. If the stack is empty,
`None` is returned.
"""
try:
return self._local.stack[-1]
# 對象.stack[-1],調用_local對象中的__getattr__方法
except (AttributeError, IndexError):
return None
進入到_local
對象中,找到__getattr__
方法:
def __getattr__(self, name): # name = stack
try:
# _request_ctx_stack._local = [ {9528:{stack :[ctx(request,session)]}},get_ident]
# 在實例內部別忘了("__storage__", "__ident_func__")與上面數組的小標是對應的
return self.__storage__[self.__ident_func__()][name]
# self.__ident_func__()獲取線程id9528,name就是stack
# 拿到[ctx(request,session)] ,然後返回
except KeyError:
raise AttributeError(name)
回到top
這裏:
def top(self): # self._local = [ {9528:{stack :[ctx(request,session)]}},get_ident]
"""The topmost item on the stack. If the stack is empty,
`None` is returned.
"""
try:
return self._local.stack[-1] # 返回 ctx (request,session)
# 對象.stack[-1],調用_local對象中的__getattr__方法
except (AttributeError, IndexError):
return None
再回到_lookup_req_object
這裏:
def _lookup_req_object(name): # name = request
top = _request_ctx_stack.top # 拿到 ctx (request,session)
# _request_ctx_stack._local = [ {9528:{stack :[ctx(request,session)]}},get_ident]
# 在實例內部別忘了("__storage__", "__ident_func__")與上面數組的小標是對應的
if top is None:
raise RuntimeError(_request_ctx_err_msg)
return getattr(top, name) # # name = request
回到partial
這裏:
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app)
request = LocalProxy(partial(_lookup_req_object, "request")) #partial 偏函數
# 取出request對象
session = LocalProxy(partial(_lookup_req_object, "session"))
g = LocalProxy(partial(_lookup_app_object, "g"))
進入到LocalProxy
中:
def __init__(self, local, name=None): # local = partial >可得> request
object.__setattr__(self, "_LocalProxy__local", local) # partial >>> request
# __setattr__爲LocalProxy這個類添加一個__local屬性
object.__setattr__(self, "__name__", name)
# local是partial偏函數,可以被執行,local中沒有__release_local__這個屬性
if callable(local) and not hasattr(local, "__release_local__"): # True
# "local" is a callable that is not an instance of Local or
# LocalManager: mark it as a wrapped function.
object.__setattr__(self, "__wrapped__", local) # partial >>> request
完了,到這裏就結束了?彆着急,還記得在Flask中可以用request.method
去判斷前端傳送數據所用的方法吧。.method
實質是調用了request
對象的__getattr__
方法,由前面可知,request
對象其實就是LocalProxy
對象,那我們進LocalProxy
對象去找找__getattr__
方法:
def __getattr__(self, name): # 例如 .method 則name = method
if name == "__members__":
return dir(self._get_current_object())
return getattr(self._get_current_object(), name) # name = method
進入到_get_current_object
中:
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__"): # True
return self.__local() # 往上翻發現,返回的就是request對象
try:
return getattr(self.__local, self.__name__)
except AttributeError:
raise RuntimeError("no object bound to %s" % self.__name__)
回到__getattr__
這裏,一切都明白了:
def __getattr__(self, name): # 例如 .method 則name = method
if name == "__members__":
return dir(self._get_current_object())
return getattr(self._get_current_object(), name) # name = method
# self._get_current_object() 返回一個request對象
請求中的所有數據都是通過反射的方式,從request中獲取的
總結
Flask上下文管理全流程解析: