一、一些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()
三、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上下文管理圖解