摘要:本文是我在學(xué)習(xí)中對上下文和幾個類似全局變量的思考和研究,也有我自己的理解在內(nèi)。為了研究中的,我找到定義在的源碼可以看到主要由等組成,我先來分析和其實和原理上是一樣的,所以將其歸為一類,稱為請求上下文。
本文是我在學(xué)習(xí)flask中對上下文和幾個類似全局變量的思考和研究,也有我自己的理解在內(nèi)。
為了研究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"),最外層是一個偏函數(shù),不過這不是重點,它主要是將"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可以看到,這個函數(shù)的主要功能是從top中取出鍵值為"request"的內(nèi)容,top是一個字典,top從_request_ctx_stack.top中來,在上面的源碼中 _request_ctx_stack = LocalStack(),從名字來看LocalStack應(yīng)該是一個棧類,應(yīng)該有pop,push,top方法,我繼續(xù)找到源碼:
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(),對應(yīng)另一個類,繼續(xù)看源碼:
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}
我截取了幾個重要的函數(shù),LocalStack()中的push,用到了Local()中的__setattr__();pop用到了__getattr__(),看到push和pop都是對"stack"這個鍵值進行查詢和賦值,我們轉(zhuǎn)到Local()這個類中,這個類有兩個實例屬性,__storage__和__ident_func__,前者是一個字典,后者是一個函數(shù),我們看一下這個get_ident函數(shù),查看源碼:
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
顯然這個函數(shù)不是python寫的,因為它來自_thread.py,是一個底層庫,從名字可以猜到和線程有關(guān),根據(jù)描述,它返回一個非零整數(shù),代表了當前線程id,我們再看看__setattr__這個方法,它其實是一個字典嵌套列表再嵌套字典的數(shù)據(jù),__storage__是一個字典,它里面的鍵值被賦值為當前線程id,這個鍵值對應(yīng)的值是另一個字典:{"stack":["request":r_val,"session":s_val]},這樣和前面聯(lián)系起來就很好理解了,Local()中的__storage__存儲的格式為{thread_id:{"stack":["request":r_val,"session":s_val]}},LocalStack()中的top方法,返回了"stack"中最后一個加入的元素,也就是最新的元素,我自己理解為服務(wù)器接受的最新的請求,在框架外看起來request和session是一個全局變量,其實內(nèi)部已經(jīng)由進程id將其分隔開了,即使同時有多個請求過來,進程間的數(shù)據(jù)也不會混亂。
同理current_app和g也一樣,唯一不同的是,current_app、g和request、session是兩個不同的實例,注意前面 _request_ctx_stack = LocalStack()、_app_ctx_stack = LocalStack(),所以"stack"中存的數(shù)據(jù)也不一樣,current_app和g稱為應(yīng)用上下文,兩者還是有區(qū)別的。
LocalProxy 則是一個典型的代理模式實現(xiàn),它在構(gòu)造時接受一個 callable 的參數(shù)(比如一個函數(shù)),這個參數(shù)被調(diào)用后的返回值本身應(yīng)該是一個 Thread Local 對象。對一個 LocalProxy 對象的所有操作,包括屬性訪問、方法調(diào)用(當然方法調(diào)用就是屬性訪問)甚至是二元操作,都會轉(zhuǎn)發(fā)到那個 callable 參數(shù)返回的 Thread Local 對象上。
LocalProxy 的一個使用場景是 LocalStack 的 call 方法。比如 my_local_stack 是一個 LocalStack 實例,那么 my_local_stack() 能返回一個 LocalProxy 對象,這個對象始終指向 my_local_stack 的棧頂元素。如果棧頂元素不存在,訪問這個 LocalProxy 的時候會拋出 RuntimeError。
需要注意的是,如果需要離線編程,尤其在寫測試代碼時,需要將應(yīng)用上下文push到棧中去,不然current_app會指向空的_app_ctx_stack棧頂,自然也就無法工作了。
我們可以通過current_app的值來判斷是否進入應(yīng)用上下文中,可以用app.app_context().push()來進入應(yīng)用上下文。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/44734.html
摘要:主要的作用是將維護的字典中鍵為對應(yīng)的值定義為。如果沒有,則會將當前到中,同時將加入列表中否則添加。注意清理之后的動作。上述代碼涉及到,它強調(diào)必須是一個可調(diào)用對象。后期的工作之一是了解。這僅僅是我的個人理解。實際上這是解決多個實例運行的問題。 Flask 中的上下文對象 知乎問題 編程中什么是「Context(上下文)」 已經(jīng)能夠簡單地說明什么是 Context,它是一個程序需要的外部對...
摘要:值得注意的是這個對象在創(chuàng)建時,將實例的本身作為實參傳入自身,因此,。到這里想必已經(jīng)很清楚了就是指對象就是對應(yīng)每次請求創(chuàng)建的對象通過將與關(guān)聯(lián)起來總結(jié)創(chuàng)建了,這個對應(yīng)的上下文,就是每響應(yīng)一個請求,就會創(chuàng)建一個對象,這個對象對應(yīng)的上下文,就是 引言 本文主要梳理了flask的current_app, request, session, g的實現(xiàn)原理 源碼說明 本文使用flask 0.5 版本...
摘要:最近在開發(fā)的時候遇到這樣一個問題我就好奇了這樣還報不在中的錯沒有顯示調(diào)用啊加一行測試無奈,一個一個翻到之間調(diào)用的每一個函數(shù),終于在找到可疑點但是這里也沒有顯式提交。為什么接下來總結(jié)下大神們的探討。 最近在開發(fā)mdwiki的時候遇到這樣一個問題.Post is unbond to session.我就好奇了 post=Post.query.filter_by(location=locat...
摘要:的上下文對象有兩種上下文,分別是請求上下文請求的對象,封裝了請求的內(nèi)容,生命周期請求處理完就結(jié)束了根據(jù)請求中的,重新載入該訪問者相關(guān)的會話信息應(yīng)用上下文處理請求時用作臨時存儲的對象。 Werkzeugs 是 Flask 的底層WSGI庫。 什么是WSGI? showImg(https://s1.ax1x.com/2018/11/13/iOqdKS.jpg); 一段簡單的app: def...
摘要:但是這些對象和全局變量不同的是它們必須是動態(tài)的,因為在多線程或者多協(xié)程的情況下,每個線程或者協(xié)程獲取的都是自己獨特的對象,不會互相干擾。中有兩種上下文和。就是實現(xiàn)了類似的效果多線程或者多協(xié)程情況下全局變量的隔離效果。 這是 flask 源碼解析系列文章的其中一篇,本系列所有文章列表: flask 源碼解析:簡介 flask 源碼解析:應(yīng)用啟動流程 flask 源碼解析:路由 flas...
閱讀 2686·2023-04-25 15:15
閱讀 1327·2021-11-25 09:43
閱讀 1614·2021-11-23 09:51
閱讀 1089·2021-11-12 10:36
閱讀 2891·2021-11-11 16:55
閱讀 966·2021-11-08 13:18
閱讀 736·2021-10-28 09:31
閱讀 2061·2019-08-30 15:47