1. 程式人生 > 程式設計 >Python Flask上下文管理機制例項解析

Python Flask上下文管理機制例項解析

前言

上下文這個概念多見於文章中,是一句話中的語境,也就是語言環境。一句莫名其妙的話出現會讓人不理解什麼意思,如果有語言環境的說明,則會更好,這就是語境對語意的影響。

上下文是一種屬性的有序序列,為駐留在環境內的物件定義環境。在物件的啟用過程中建立上下文,物件被配置為要求某些自動服務,如同步、事務、實時啟用、安全性等等。

如在計算機中,相對於程序而言,上下文就是程序執行時的環境。具體來說就是各個變數和資料,包括所有的暫存器變數、程序開啟的檔案、記憶體資訊等。可以理解上下文是環境的一個快照,是一個用來儲存狀態的物件。在程式中我們所寫的函式大都不是單獨完整的,在使用一個函式完成自身功能的時候,很可能需要同其他的部分進行互動,需要其他外部環境變數的支援,上下文就是給外部環境的變數賦值,使函式能正確執行。

請求上下文

關於WSGI

WSGI(全稱Web Server Gateway Interface),是為 Python 語言定義的Web伺服器和Web應用程式之間的一種簡單而通用的介面,它封裝了接受HTTP請求、解析HTTP請求、傳送HTTP,響應等等的這些底層的程式碼和操作,使開發者可以高效的編寫Web應用。

Flask提供了兩種上下文,一種是應用上下文(Application Context),一種是請求上下文(Request Context)。

  • RequestContext 請求上下文
  • Request 請求的物件,封裝了Http請求(environ)的內容
  • Session 根據請求中的cookie,重新載入該訪問者相關的會話資訊。
  • AppContext 程式上下文
  • g 處理請求時用作臨時儲存的物件。每次請求都會重設這個變數
  • current_app 當前啟用程式的程式例項

參見Flask上下文官方文件 請求上下文 和 應用上下文.

  1. application 指的就是當你呼叫app = Flask(__name__)建立的這個物件app;

  2.request 指的是每次http請求發生時,WSGI server(比如gunicorn)調Flask.call()之後,在Flask物件內部建立的Request物件;

  3.application 表示用於響應WSGI請求的應用本身,request 表示每次http請求;

  4.application的生命週期大於request,一個application存活期間,可能發生多次http請求,所以,也就會有多個request

生命週期

  • current_app的生命週期最長,只要當前程式例項還在執行,都不會失效。
  • Request和g的生命週期為一次請求期間,當請求處理完成後,生命週期也就完結了
  • Session就是傳統意義上的session了。只要它還未失效(使用者未關閉瀏覽器、沒有超過設定的失效時間),那麼不同的請求會共用同樣的session。

Flask處理流程

Python Flask上下文管理機制例項解析

local執行緒隔離物件

不用local物件的情況

from threading import Thread

request = '123'

class MyThread(Thread):
  def run(self):
    global request
    request = 'abc'
    print('子執行緒',request)  #子執行緒 abc

mythread = MyThread()
mythread.start()
mythread.join()

print('主執行緒',request)     #主執行緒 abc

如果用local物件,在每個執行緒中都是隔離的

from threading import Thread
from werkzeug.local import Local

locals = Local()
locals.request = '123'

class MyThread(Thread):
  def run(self):
    locals.request = 'abc'
    print('子執行緒',locals.request)  #子執行緒 abc

mythread = MyThread()
mythread.start()
mythread.join()

print('主執行緒',locals.request)     #主執行緒 123

app上下文和request上下文

應用上下文和請求上下文都是存放在一個‘LocalStack'的棧中,和應用app相關的操作就必須要用到應用上下文,比如通過current_app獲取當前的這個app的名字。和請求相關的操作就必須用到請求上下文,比如使用url_for反轉檢視函式。

在檢視函式中,不用擔心上下文的問題,因為檢視函式要執行,name肯定是通過訪問url的方式執行的,name這種情況下,Flask底層就已經自動的幫我們把請求上年文和應用上下文都推入到了相應的棧中。如果想要在檢視函式外面執行相關的操作,name就必須要手動推入相關的上下文手動推入請求上下文:推入請求上下文到棧中,會首先判斷有沒有應用上下文,如果沒有那麼就會先推入應用上下文到棧中,然後再推入請求上下文到棧中。

app上下文

from flask import Flask,current_app

app = Flask(__name__)

#如果在檢視函式外部訪問,則必須手動推入一個app上下文到app上下文棧中
#第一種方法
# app_context = app.app_context()
# app_context.push()
# print(current_app.name)

#第二種方法
with app.app_context():
  print(current_app.name)   #context_demo

@app.route('/')
def index():
  # 在檢視函式內部可以直接訪問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_for

app = Flask(__name__)

#應用上下文
#如果在檢視函式外部訪問,則必須手動推入一個app上下文到app上下文棧中
with app.app_context():
  print(current_app.name)   #context_demo

@app.route('/')
def index():
  # 在檢視函式內部可以直接訪問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():
  # 手動推入一個請求上下文到請求上下文棧中
  # 如果當前應用上下文棧中沒有應用上下文
  # 那麼會首先推入一個應用上下文到棧中
  print(url_for('my_list'))

if __name__ == '__main__':
  app.run(debug=True)

為什麼上下文需要放在棧中?

1.應用上下文:

Flask底層是基於werkzeug,werkzeug是可以包含多個app的,所以這時候用一個棧來儲存,如果你在使用app1,那麼app1應該是要在棧的頂部,如果用完了app1那麼app應該從棧中刪除,方便其他程式碼使用下面的app。

2.應用上下文:

如果在寫測試程式碼,或者離線指令碼的時候,我們有時候可能需要建立多個請求上下文,這時候就需要存放到一個棧中了。使用哪個請求上下文的時候,就把對應的請求上下文放到棧的頂部,用完了就要把這個請求上下文從棧中移除掉。

執行緒隔離的g物件

g物件是在整個Flask應用執行期間都是可以使用的,並且它也是跟request一樣是執行緒隔離的。這個物件是專門用來儲存開發者自定義的一些資料,方便在整個Flask程式中都可以使用。一般使用就是,將一些經常會用到的資料繫結到上面,以後就直接從g上面取就可以了,而不是通過傳參的形式,這樣更加方便。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。