Flask上下文管理、session原理和全局g對象


一、一些python的知識

1、偏函數

def add(x, y, z):
    print(x + y + z)


# 原本的寫法:x,y,z可以傳任意數字
add(1,2,3)


# 如果我要實現一個功能,這三個數中,其中一個數必須是3
# 我們就可以使用偏函數來幫着我們傳參
from functools import partial

# partial:給add這個函數固定傳一個數字 3
new_add = partial(add, 3)

# 因此新的函數只需要傳2個參數
new_add(1,1)
new_add(1,2)


# 偏函數:就是幫我們給一個函數固定一個參數
# new_add(x1, x2) --> add(3, x1, x2)

 

2、類的兩個雙下方法

1. __getattr__:對象獲取屬性值的時候觸發

2. __setattr__:對象設置屬性值的時候觸發

3.示例
class A(object):
    def __init__(self):
        # self.name = {}
        # 初始化的時候,給這個對象設置一個屬性名為name,值為空的字典
        object.__setattr__(self, "name", {})

    def __getattr__(self, item):
        print("getattr: ", item)

    def __setattr__(self, key, value):
        print("setattr: ", self.name)
        print("setattr: ", key, value)
        self.name[key] = value
        print("setattr: ", self.name)


# 實例化,調用__init__
a = A()

# 獲取對象某個屬性的值,會調用__getattr__
# 如果A這個類沒有__getattr__,就會去執行父類的__getattr__
# 但是嚴謹的__getattr__是:如果你沒有這個屬性,就會給你報錯
# 我們可以在A類重寫__getattr__,可以讓它不報錯
a.xxx  # getattr:  xxx

# 給對象的某個屬性設置值,會調用__setattr__
# 執行的邏輯跟__getattr__一樣,A類沒有就去調用父類的
a.xxx = '小明'
# 首先打印name字典的默認值:是個空字典 setattr:  {}
# setattr的key是左邊的變量,value是右邊的值:setattr:  xxx 小明
# 打印self.name這個字典:setattr:  {'xxx': '小明'}

 

二、Flask上下文管理

Flask的上下文管理我們可以理解為一個生命周期
也就是請求進來到請求出去一共做了哪些事情
首先我們知道項目啟動執行了app.run()方法,調用了werkzeug的run_simple()方法
run_simple(host, port, self, **options) 這時候的self就是我們的app
run_simple會執行self(),也就是app(), 那么app = Flask() 所以會走Flask的__call__方法

那么__call__做了什么呢

environ是我們請求來的原始數據~當成參數傳遞給了request_context方法

 


進入這個RequestContext對象

 


這是初始化這個類做的一些事情
在這里重新封裝了request, 以及給session 賦值了 None
也就是說:
ctx = RequestContext(app, environ)
ctx.request 是重新封裝的request
ctx.session = None

 

繼續


執行了_request_ctx_stack.push(ctx)
也就是說_request_ctx_stack它把我們的ctx對象push到了一個地方
我們的ctx這個對象里面有request以及session等

 


這個初始化方法就是剛才python類的雙下方法__setattr__
就是給Local類初始化了兩個屬性    __storage__ = {}            __ident_func__ = get_ident

 

我們繼續看LocalStark中push方法做了什么


現在回去看wsgi_app里的ctx.push(),到這里,它就走完了,接下來就要走視圖
那到這里我們可以通過什么樣的方法在我們視圖中拿到這個request對象呢
request在ctx對象里能通過ctx.request得到,那我們怎么得到ctx呢
ctx被LocalStack對象放入到Local中了

from flask import Flask
from flask import globals

app = Flask(__name__)


@app.route("/")
def index():
    ctx = globals._request_ctx_stack.top
    print(ctx.request.method)
    return "index"



if __name__ == '__main__':
    app.run()
獲取ctx

 

三、Flask上下文管理(續)

這個request:
  from flask.globals import _request_ctx_stack
  ctx = _request_ctx_stack.top
  request = ctx.request


和這個request:
  from flask import request


兩個request有什么區別?


其實我們導入的request跟我們上面拿到的request是一樣的。

下面看看怎么直接拿request

reqeust是LocalProxy這個類的實例化對象,參數是一個偏函數,
那當我們調用request.method 等方法的時候走的是LocalProxy這個類的__getattr__方法

這里的_get_current_object()相當於我們偏函數的執行

 

因此,直接導入的request也是通過LocalStack方法去Local中取ctx對象
然后通過getattr 找到ctx.request,
也就是說這個LocalProxy就是一個幫助我們取值的代理,讓我們的取值變的更加簡單
這個代理通過偏函數來綁定參數,
ctx中封裝了request,以及session,只不過到這里我們的session依然是空的。

 

四、session的實現原理

 _request_ctx_stack.push(self)走完后,會繼續走這個

也就是說,請求進來把ctx放入Local中后,從前端解密了cookie,然后把解密數據好的數據給了self.session
繼續走

那么session的實現機制:
  1. 請求進來獲取cookie的值
  2. 解密cookie轉換成字典(沒有cookie就是空字典)賦值給ctx.session
  3. 當請求走的時候把session的數據加密
  4. 設置cookie

 

五、應用上下文管理

 應用上下文和請求上下文的原理和源碼是一樣的

ctx.push()

也就是說,我們請求上下文和應用上下文,分別建立了兩個Local對象
兩個Local對象數據結構都是一樣的,那么請求上下文和應用上下文為什么要分開存放呢
因為我們寫離線腳本的時候需要用到!

 

小結:
也就是說可以導入請求上下文的request, session和應用上下文的g, current_app
from flask import Flask, request, session, g, current_app

 

六、全局對象g

我們說應用上下文里封裝了g對象,那么這個g對象是什么呢
1. Flask中g的生命周期?
  我們講這么多上下文管理,我們知道請求進來會為每個請求在Local中建立一個獨立空間
  也就是在應用上下文的Local對象中建立了一個g對象,當請求走的時候就會刪除
  所以g的生命周期是一次請求的進來到離開。

 

2. g和session有什么區別?
  session有cookie,下次請求進來的時候能帶數據過來

 

3. g和全局對象有什么區別?
  全局變量,是在項目啟動創建的,直到項目停止才會銷毀
  無論多少請求進來都可以訪問全局變量
  而我們的g對象一般情況用於before_request中設置值,只為這一次請求建立全局變量

 

4. Demo

from flask import Flask, request, session, g, current_app
from flask.globals import _request_ctx_stack


app = Flask(__name__)


@app.before_request
def auth():
    g.xxx = "小明"


@app.route("/")
def index():
    ctx = _request_ctx_stack.top
    print(ctx.request)
    print(ctx.request.method)
    print(current_app)
    # request.xxx的執行過程
    # 1. request --> LocalProxy(偏函數)
    # 2. request.xxx --> LocalProxy  __getattr__
    # 3. __getattr__  --> getattr(偏函數的執行,xxx )
    # 4. 偏函數-->  _request_ctx_stack.top.request

    print(g.xxx)
    return "INDEX"


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

 

七、Flask上下文管理圖解

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM