一、threading-local
1、threding-local
作用:為每一個線程開辟一塊空間進行數據存儲
from threading import local from threading import Thread import time # 示例化local對象 ret=local() def task(s): global ret ret.value=s time.sleep(2) print(ret.value) # 開啟10個線程 for i in range(10): t=Thread(target=task,args=(i,)) t.start()
2、自定義local
# 如果有協程則使用協程唯一標識getcurrent
try:
from greenlet import getcurrent as get_ident
except Exception as e:
from threading import Thread,get_ident
class Local(object):
# 線程的唯一標識
ident = get_ident()
def __init__(self):
# 執行父類__setattr__
object.__setattr__(self,"storage",{})
def __setattr__(self,k, v):
"""
構造dict
storage={
ident:{val:0},
ident:{val:1},
ident:{val:3},
ident:{val:4},
}
"""
if self.ident in self.storage:
self.storage[self.ident][k] = v
else:
self.storage[self.ident] = {k: v}
def __getattr__(self,k):
return self.storage[self.ident][k]
obj=Local()
def task(arg):
# 執行__setattr__
obj.var=arg
# 執行__getattr__
v=obj.var
print(v)
for i in range(10):
t = Thread(target=task,args=(i,))
t.start()
二、上下文管理源碼分析
1、上下文管理本質(類似於threading.local)
1、每一個線程都會在Local類中創建一條數據
{
“唯一標識”:{stark:[ctx,]}
“唯一標識”:{stark:[ctx,]}
}
2、當請求進來之后,將請求相關數據添加到列表里面[request,],以后如果使用時,就去讀取 3、列表中的數據,請求完成之后,將request從列表中移除
2、在源碼中分析上下文管理 第一階段:執行__call__--->app.wsgi-->將ctx(request,session)封裝為RequestContent()在(open_session), app_ctx(g,app)封裝為APPContent()通過LocalStack將這兩個類放入Local對象中 第二階段:視圖函數導入:request/session/g/app ,通過偏函數(_lookup_req_object)在通過(LocalProxy())去LocalStack中的Local類中對其進行增刪改查操作
第三階段:請求處理完畢
- 通過save_session將簽名session保存到cookie
-通過ctx.pop()去LocalStack中的Local類- 將ctx刪除
有關面試問題
問題一:flask和django的區別:
對於django來說,內部組件特別多,自身功能強大,有點大而全,而flask,內置組件很少,但是它的第三方組件很多,擴展性強,有點短小精悍,而它們之間也有相似之處,
因為它們兩個框架都沒有寫sockte,都是基於wsgi協議做的,在此之外,flask框架中的上下文管理較為耀眼。
相同點:它們兩個框架都沒有寫sockte,都是基於wsgi協議做的
請求相關數據傳遞的方式不同:django:通過傳遞request參數取值
flask:見問題二
組件不同:django組件多
flask組件少,第三方組件豐富
問題1.1: flask上下文管理:
簡單來說,falsk上下文管理可以分為三個階段:
1、請求進來時,將請求相關的數據放入上下問管理中
2、在視圖函數中,要去上下文管理中取值
3、請求響應,要將上下文管理中的數據清除
詳細點來說:
1、請求剛進來,將request,session封裝在RequestContext類中,app,g封裝在AppContext類中,並通過LocalStack將requestcontext和appcontext放入Local類中
2、視圖函數中,通過localproxy--->偏函數--->localstack--->local取值
3、請求相應時,先執行save.session()再各自執行pop(),將local中的數據清除
問題1.2 flask第三方組件
flask:
-flask-session 默認放入cookie,可以放入redis
-flask-redis
-flask-migrate
-flask-script
-blinker 信號
公共: DBUtils 數據庫連接池
wtforms 表單驗證+生成HTML標簽
sqlalchemy
自定義:Auth 參考falsk-login
問題二:Flask中的session是什么時候創建,什么時候銷毀的?
當請求進來時,會將requset和session封裝為一個RequestContext對象,通過LocalStack將RequestContext放入到Local對象中,因為
請求第一次來session是空值,所以執行open_session,給session(uuid4())賦值,再通過視圖函數處理,請求響應時執行save.session,將簽名session寫入cookie中,再講Local中的數值pop掉。
問題三:flask中一共有幾個LocalStack和Local對象
兩個LocalStack,兩個Local
request、session共同用一個LocalStack和Local
g、app共同用一個Localstack和Local
問題四: 為什么把請求放到RequestContext中:
因為request和session都是在視圖中操作頻繁的數據,也是用戶請求需要用的數據,將request和session封裝在RequestContext中top,pop一次就可以完成,而單獨不封裝在一起就會多次操作,
ctx = RequestContext(request,session)
問題五:local作用
-保存 請求上下文對象和app上下文對象
-localstack的源碼與threading.local(線程處理)作用相似,不同之處是Local是通過greenlet(協程)獲取唯一標識,粒度更細
問題六:Localstack作用
2、將local對象中的數據維護成一個棧【ctx,ctx】(先進后出)
{
“協程或線程的唯一標識”: { stack:[ctx,ctx,ctx,] }
}
為什么維護成一個棧?
當是web應用時:不管是單線程還是多線程,棧中只有一個數據
- 服務端單線程:
{
111:{stack: [ctx, ]}
}
- 服務端多線程:
{
111:{stack: [ctx, ]}
112:{stack: [ctx, ]}
}
離線腳本:可以在棧中放入多個數據
with app01.app_context():
print(current_app)
with app02.app_context():
print(current_app)
print(current_app)
問題七:什么是g?
g 相當於一次請求的全局變量,當請求進來時將g和current_app封裝為一個APPContext類,在通過LocalStack將Appcontext放入Local中,取值時通過偏函數,LocalStack、loca l中取值,響應時將local中的g數據刪除:
問題八:怎么獲取Session/g/current_app/request
通過 、偏函數(lookup_req_object)、Localstack、Local取值
問題九: 技術點:
- 反射 (LocalProxy())
- 面向對象,封裝:RequestContext
- 線程(threading.local)
- 筆試:自己寫一個類+列表 實現棧。(LocalStack)
問題十:python基本哪些內容比較重要:
1、反射
-CBV
-django配置文件
-wtforms中的Form()示例化中 將"_fields中的數據封裝到From類中"
2、裝飾器 (迭代器,生成器)
-flask:路由、裝飾器
-認證
-csrf
3、面向對象
-繼承、封裝、多態(簡單描述)
-雙下划線:
__mro__ wtform中 FormMeta中繼承類的優先級
__dict__
__new__ ,實例化但是沒有給當前對象
wtforms,字段實例化時返回:不是StringField,而是UnboundField
rest frawork many=Turn 中的序列化
__call__
flask 請求的入口app.run()
字段生成標簽時:字段.__str__ => 字段.__call__ => 插件.__call__
__iter__ 循環對象是,自定義__iter__
wtforms中BaseForm中循環所有字段時定義了__iter__
metaclass
- 作用:用於指定當前類使用哪個類來創建
- 場景:在類創建之前定制操作
示例:wtforms中,對字段進行排序。