Python Flask上下文管理機(jī)制實(shí)例解析
前言
上下文這個(gè)概念多見于文章中,是一句話中的語境,也就是語言環(huán)境。一句莫名其妙的話出現(xiàn)會(huì)讓人不理解什么意思,如果有語言環(huán)境的說明,則會(huì)更好,這就是語境對語意的影響。
上下文是一種屬性的有序序列,為駐留在環(huán)境內(nèi)的對象定義環(huán)境。在對象的激活過程中創(chuàng)建上下文,對象被配置為要求某些自動(dòng)服務(wù),如同步、事務(wù)、實(shí)時(shí)激活、安全性等等。
如在計(jì)算機(jī)中,相對于進(jìn)程而言,上下文就是進(jìn)程執(zhí)行時(shí)的環(huán)境。具體來說就是各個(gè)變量和數(shù)據(jù),包括所有的寄存器變量、進(jìn)程打開的文件、內(nèi)存信息等。可以理解上下文是環(huán)境的一個(gè)快照,是一個(gè)用來保存狀態(tài)的對象。在程序中我們所寫的函數(shù)大都不是單獨(dú)完整的,在使用一個(gè)函數(shù)完成自身功能的時(shí)候,很可能需要同其他的部分進(jìn)行交互,需要其他外部環(huán)境變量的支持,上下文就是給外部環(huán)境的變量賦值,使函數(shù)能正確運(yùn)行。
請求上下文
關(guān)于WSGI
WSGI(全稱Web Server Gateway Interface),是為 Python 語言定義的Web服務(wù)器和Web應(yīng)用程序之間的一種簡單而通用的接口,它封裝了接受HTTP請求、解析HTTP請求、發(fā)送HTTP,響應(yīng)等等的這些底層的代碼和操作,使開發(fā)者可以高效的編寫Web應(yīng)用。
Flask提供了兩種上下文,一種是應(yīng)用上下文(Application Context),一種是請求上下文(Request Context)。
RequestContext 請求上下文 Request 請求的對象,封裝了Http請求(environ)的內(nèi)容 Session 根據(jù)請求中的cookie,重新載入該訪問者相關(guān)的會(huì)話信息。 AppContext 程序上下文 g 處理請求時(shí)用作臨時(shí)存儲(chǔ)的對象。每次請求都會(huì)重設(shè)這個(gè)變量 current_app 當(dāng)前激活程序的程序?qū)嵗?p>參見Flask上下文官方文檔 請求上下文 和 應(yīng)用上下文.1. application 指的就是當(dāng)你調(diào)用app = Flask(__name__)創(chuàng)建的這個(gè)對象app;
2.request 指的是每次http請求發(fā)生時(shí),WSGI server(比如gunicorn)調(diào)Flask.call()之后,在Flask對象內(nèi)部創(chuàng)建的Request對象;
3.application 表示用于響應(yīng)WSGI請求的應(yīng)用本身,request 表示每次http請求;
4.application的生命周期大于request,一個(gè)application存活期間,可能發(fā)生多次http請求,所以,也就會(huì)有多個(gè)request
生命周期
current_app的生命周期最長,只要當(dāng)前程序?qū)嵗€在運(yùn)行,都不會(huì)失效。 Request和g的生命周期為一次請求期間,當(dāng)請求處理完成后,生命周期也就完結(jié)了 Session就是傳統(tǒng)意義上的session了。只要它還未失效(用戶未關(guān)閉瀏覽器、沒有超過設(shè)定的失效時(shí)間),那么不同的請求會(huì)共用同樣的session。Flask處理流程
local線程隔離對象
不用local對象的情況
from threading import Threadrequest = ’123’class MyThread(Thread): def run(self): global request request = ’abc’ print(’子線程’,request) #子線程 abcmythread = MyThread()mythread.start()mythread.join()print(’主線程’,request) #主線程 abc
如果用local對象,在每個(gè)線程中都是隔離的
from threading import Threadfrom werkzeug.local import Locallocals = Local()locals.request = ’123’class MyThread(Thread): def run(self): locals.request = ’abc’ print(’子線程’,locals.request) #子線程 abcmythread = MyThread()mythread.start()mythread.join()print(’主線程’,locals.request) #主線程 123
app上下文和request上下文
應(yīng)用上下文和請求上下文都是存放在一個(gè)‘LocalStack’的棧中,和應(yīng)用app相關(guān)的操作就必須要用到應(yīng)用上下文,比如通過current_app獲取當(dāng)前的這個(gè)app的名字。和請求相關(guān)的操作就必須用到請求上下文,比如使用url_for反轉(zhuǎn)視圖函數(shù)。
在視圖函數(shù)中,不用擔(dān)心上下文的問題,因?yàn)橐晥D函數(shù)要執(zhí)行,name肯定是通過訪問url的方式執(zhí)行的,name這種情況下,F(xiàn)lask底層就已經(jīng)自動(dòng)的幫我們把請求上年文和應(yīng)用上下文都推入到了相應(yīng)的棧中。如果想要在視圖函數(shù)外面執(zhí)行相關(guān)的操作,name就必須要手動(dòng)推入相關(guān)的上下文手動(dòng)推入請求上下文:推入請求上下文到棧中,會(huì)首先判斷有沒有應(yīng)用上下文,如果沒有那么就會(huì)先推入應(yīng)用上下文到棧中,然后再推入請求上下文到棧中。
app上下文
from flask import Flask,current_appapp = Flask(__name__)#如果在視圖函數(shù)外部訪問,則必須手動(dòng)推入一個(gè)app上下文到app上下文棧中#第一種方法# app_context = app.app_context()# app_context.push()# print(current_app.name)#第二種方法with app.app_context(): print(current_app.name) #[email protected](’/’)def index(): # 在視圖函數(shù)內(nèi)部可以直接訪問current_app.name print(current_app.name) #context_demo return ’Hello World!’if __name__ == ’__main__’: app.run(debug=True)
request請求上下文
from flask import Flask,current_app,url_forapp = Flask(__name__)#應(yīng)用上下文#如果在視圖函數(shù)外部訪問,則必須手動(dòng)推入一個(gè)app上下文到app上下文棧中with app.app_context(): print(current_app.name) #[email protected](’/’)def index(): # 在視圖函數(shù)內(nèi)部可以直接訪問current_app.name print(current_app.name) #context_demo return ’Hello World!’@app.route(’/list/’)def my_list(): return ’my_list’# 請求上下文with app.test_request_context(): # 手動(dòng)推入一個(gè)請求上下文到請求上下文棧中 # 如果當(dāng)前應(yīng)用上下文棧中沒有應(yīng)用上下文 # 那么會(huì)首先推入一個(gè)應(yīng)用上下文到棧中 print(url_for(’my_list’))if __name__ == ’__main__’: app.run(debug=True)
為什么上下文需要放在棧中?
1.應(yīng)用上下文:
Flask底層是基于werkzeug,werkzeug是可以包含多個(gè)app的,所以這時(shí)候用一個(gè)棧來保存,如果你在使用app1,那么app1應(yīng)該是要在棧的頂部,如果用完了app1那么app應(yīng)該從棧中刪除,方便其他代碼使用下面的app。
2.應(yīng)用上下文:
如果在寫測試代碼,或者離線腳本的時(shí)候,我們有時(shí)候可能需要?jiǎng)?chuàng)建多個(gè)請求上下文,這時(shí)候就需要存放到一個(gè)棧中了。使用哪個(gè)請求上下文的時(shí)候,就把對應(yīng)的請求上下文放到棧的頂部,用完了就要把這個(gè)請求上下文從棧中移除掉。
線程隔離的g對象
g對象是在整個(gè)Flask應(yīng)用運(yùn)行期間都是可以使用的,并且它也是跟request一樣是線程隔離的。這個(gè)對象是專門用來存儲(chǔ)開發(fā)者自定義的一些數(shù)據(jù),方便在整個(gè)Flask程序中都可以使用。一般使用就是,將一些經(jīng)常會(huì)用到的數(shù)據(jù)綁定到上面,以后就直接從g上面取就可以了,而不是通過傳參的形式,這樣更加方便。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持好吧啦網(wǎng)。
相關(guān)文章:
1. CSS3實(shí)例分享之多重背景的實(shí)現(xiàn)(Multiple backgrounds)2. XHTML 1.0:標(biāo)記新的開端3. HTML5 Canvas繪制圖形從入門到精通4. XML解析錯(cuò)誤:未組織好 的解決辦法5. ASP基礎(chǔ)知識VBScript基本元素講解6. asp(vbscript)中自定義函數(shù)的默認(rèn)參數(shù)實(shí)現(xiàn)代碼7. 詳解CSS偽元素的妙用單標(biāo)簽之美8. 利用CSS3新特性創(chuàng)建透明邊框三角9. 使用Spry輕松將XML數(shù)據(jù)顯示到HTML頁的方法10. XML入門的常見問題(四)
