摘要:本文是我在學習中對上下文和幾個類似全局變量的思考和研究,也有我自己的理解在內。為了研究中的,我找到定義在的源碼可以看到主要由等組成,我先來分析和其實和原理上是一樣的,所以將其歸為一類,稱為請求上下文。
本文是我在學習flask中對上下文和幾個類似全局變量的思考和研究,也有我自己的理解在內。
為了研究flask中的current_app、g、request、session,我找到定義在global.py的源碼:
# context locals _request_ctx_stack = LocalStack() _app_ctx_stack = LocalStack() 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"))
可以看到主要由_lookup_req_object、_lookup_app_object、_find_app等組成,我先來分析request和session
其實request和session原理上是一樣的,所以將其歸為一類,稱為請求上下文。
我們從最里面看起,partial(_lookup_req_object, "request"),最外層是一個偏函數,不過這不是重點,它主要是將"request"傳給_lookup_req_object,沒有其他含義, 順著_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)
從最后的return可以看到,這個函數的主要功能是從top中取出鍵值為"request"的內容,top是一個字典,top從_request_ctx_stack.top中來,在上面的源碼中 _request_ctx_stack = LocalStack(),從名字來看LocalStack應該是一個棧類,應該有pop,push,top方法,我繼續找到源碼:
def __init__(self): self._local = Local() ... ... def push(self, obj): """Pushes a new item to the stack""" rv = getattr(self._local, "stack", None) if rv is None: self._local.stack = rv = [] rv.append(obj) 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: release_local(self._local) return stack[-1] else: return stack.pop() @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
可以看到LocalStack()這個類有一個屬性self._local = Local(),對應另一個類,繼續看源碼:
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}
我截取了幾個重要的函數,LocalStack()中的push,用到了Local()中的__setattr__();pop用到了__getattr__(),看到push和pop都是對"stack"這個鍵值進行查詢和賦值,我們轉到Local()這個類中,這個類有兩個實例屬性,__storage__和__ident_func__,前者是一個字典,后者是一個函數,我們看一下這個get_ident函數,查看源碼:
def get_ident(): # real signature unknown; restored from __doc__ """ get_ident() -> integer Return a non-zero integer that uniquely identifies the current thread amongst other threads that exist simultaneously. This may be used to identify per-thread resources. Even though on some platforms threads identities may appear to be allocated consecutive numbers starting at 1, this behavior should not be relied upon, and the number should be seen purely as a magic cookie. A thread"s identity may be reused for another thread after it exits. """ return 0
顯然這個函數不是python寫的,因為它來自_thread.py,是一個底層庫,從名字可以猜到和線程有關,根據描述,它返回一個非零整數,代表了當前線程id,我們再看看__setattr__這個方法,它其實是一個字典嵌套列表再嵌套字典的數據,__storage__是一個字典,它里面的鍵值被賦值為當前線程id,這個鍵值對應的值是另一個字典:{"stack":["request":r_val,"session":s_val]},這樣和前面聯系起來就很好理解了,Local()中的__storage__存儲的格式為{thread_id:{"stack":["request":r_val,"session":s_val]}},LocalStack()中的top方法,返回了"stack"中最后一個加入的元素,也就是最新的元素,我自己理解為服務器接受的最新的請求,在框架外看起來request和session是一個全局變量,其實內部已經由進程id將其分隔開了,即使同時有多個請求過來,進程間的數據也不會混亂。
同理current_app和g也一樣,唯一不同的是,current_app、g和request、session是兩個不同的實例,注意前面 _request_ctx_stack = LocalStack()、_app_ctx_stack = LocalStack(),所以"stack"中存的數據也不一樣,current_app和g稱為應用上下文,兩者還是有區別的。
LocalProxy 則是一個典型的代理模式實現,它在構造時接受一個 callable 的參數(比如一個函數),這個參數被調用后的返回值本身應該是一個 Thread Local 對象。對一個 LocalProxy 對象的所有操作,包括屬性訪問、方法調用(當然方法調用就是屬性訪問)甚至是二元操作,都會轉發到那個 callable 參數返回的 Thread Local 對象上。
LocalProxy 的一個使用場景是 LocalStack 的 call 方法。比如 my_local_stack 是一個 LocalStack 實例,那么 my_local_stack() 能返回一個 LocalProxy 對象,這個對象始終指向 my_local_stack 的棧頂元素。如果棧頂元素不存在,訪問這個 LocalProxy 的時候會拋出 RuntimeError。
需要注意的是,如果需要離線編程,尤其在寫測試代碼時,需要將應用上下文push到棧中去,不然current_app會指向空的_app_ctx_stack棧頂,自然也就無法工作了。
我們可以通過current_app的值來判斷是否進入應用上下文中,可以用app.app_context().push()來進入應用上下文。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/44734.html
摘要:主要的作用是將維護的字典中鍵為對應的值定義為。如果沒有,則會將當前到中,同時將加入列表中否則添加。注意清理之后的動作。上述代碼涉及到,它強調必須是一個可調用對象。后期的工作之一是了解。這僅僅是我的個人理解。實際上這是解決多個實例運行的問題。 Flask 中的上下文對象 知乎問題 編程中什么是「Context(上下文)」 已經能夠簡單地說明什么是 Context,它是一個程序需要的外部對...
摘要:值得注意的是這個對象在創建時,將實例的本身作為實參傳入自身,因此,。到這里想必已經很清楚了就是指對象就是對應每次請求創建的對象通過將與關聯起來總結創建了,這個對應的上下文,就是每響應一個請求,就會創建一個對象,這個對象對應的上下文,就是 引言 本文主要梳理了flask的current_app, request, session, g的實現原理 源碼說明 本文使用flask 0.5 版本...
摘要:最近在開發的時候遇到這樣一個問題我就好奇了這樣還報不在中的錯沒有顯示調用啊加一行測試無奈,一個一個翻到之間調用的每一個函數,終于在找到可疑點但是這里也沒有顯式提交。為什么接下來總結下大神們的探討。 最近在開發mdwiki的時候遇到這樣一個問題.Post is unbond to session.我就好奇了 post=Post.query.filter_by(location=locat...
摘要:的上下文對象有兩種上下文,分別是請求上下文請求的對象,封裝了請求的內容,生命周期請求處理完就結束了根據請求中的,重新載入該訪問者相關的會話信息應用上下文處理請求時用作臨時存儲的對象。 Werkzeugs 是 Flask 的底層WSGI庫。 什么是WSGI? showImg(https://s1.ax1x.com/2018/11/13/iOqdKS.jpg); 一段簡單的app: def...
摘要:但是這些對象和全局變量不同的是它們必須是動態的,因為在多線程或者多協程的情況下,每個線程或者協程獲取的都是自己獨特的對象,不會互相干擾。中有兩種上下文和。就是實現了類似的效果多線程或者多協程情況下全局變量的隔離效果。 這是 flask 源碼解析系列文章的其中一篇,本系列所有文章列表: flask 源碼解析:簡介 flask 源碼解析:應用啟動流程 flask 源碼解析:路由 flas...
閱讀 2675·2023-04-25 15:15
閱讀 1316·2021-11-25 09:43
閱讀 1604·2021-11-23 09:51
閱讀 1079·2021-11-12 10:36
閱讀 2880·2021-11-11 16:55
閱讀 955·2021-11-08 13:18
閱讀 723·2021-10-28 09:31
閱讀 2048·2019-08-30 15:47