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