Local与偏函数
threasing.local
多个线程修改同一个数据,复制多份变量给每个线程用,为每个线程开辟一块空间进行数据存储。
不使用therading.local
# 不用local from threading import Thread import time cxw = -1 def task(arg): global cxw cxw = arg # time.sleep(2) print(cxw) for i in range(10): t = Thread(target=task,args=(i,)) t.start()
结果分析:在运行时结果全部显示为9,打印的数据是混乱,当把time.sleep删除后,结果正常打印1,2,3,4....10。
使用threading.local
from threading import Thread from threading import local import time from threading import get_ident # 特殊的对象 cxw = local() def task(arg): # 对象.val = 1/2/3/4/5 cxw.value = arg time.sleep(2) print(cxw.value) for i in range(10): t = Thread(target=task,args=(i,)) t.start()
使用local()时数据能正常显示,不混乱。
通过字典自定义threaing.local(函数版)
# 线程下 获取id from threading import get_ident,Thread import time storage = {} def set(k,v): index = get_ident() if index in storage: storage[index][k] = v else: storage[index] = {k:v} def get(k): index = get_ident() return storage[index][k] def task(arg): set("val",arg) v = get('val') print(v) for i in range(10): t = Thread(target=task,args=(i,)) t.start()
面向对象版
from threading import get_ident,Thread import time class Local(object): storage = {} def set(self,k,v): ident = get_ident() if ident in Local.storage: Local.storage[ident][k] = v else: Local.storage[ident] = {k,v} def get(self,k): ident = get_ident() return Local.storage[ident][k] obj = Local() # 实例化对象 def task(arg): obj.set('val',arg) v = obj.get("val") print(v) for i in range(10): t = Thread(target=task,args=(i,)) t.start()
通过setattr和getattr实现
# 反射 # hasattr(object,name) # 判断对象是否拥有某个属性 # setattr(object,name,value) #为对象增加新的属性 # getattr(object,name,default) #从对象中获取某个属性 # delattr(object,name) #从对象中删除某个属性 # from threading import get_ident,Thread import time class Local(object): storage = {} def __setattr__(self, key, value): ident = get_ident() if ident in Local.storage: Local.storage[ident][key] = value else: Local.storage[ident] = {key:value} def __getattr__(self, key): ident = get_ident() return Local.storage[ident][key] obj = Local() def task(arg): obj.val = arg print(obj.val) for i in range(10): t = Thread(target=task,args=(i,)) t.start()
每个对象有自己的存储空间(字典)
from threading import get_ident,Thread import time class Local(object): def __init__(self): object.__setattr__(self,'storage',{}) self.storage={} def __setattr__(self, k, v): ident = get_ident() if ident in self.storage: self.storage[ident][k] = v else: self.storage[ident] = {k: v} def __getattr__(self, k): ident = get_ident() return self.storage[ident][k] obj = Local() def task(arg): obj.val = arg obj.xxx = arg print(obj.val) for i in range(10): t = Thread(target=task,args=(i,)) t.start()
try: from greenlet import getcurrent as get_ident except Exception as e: from threading import get_ident from threading import Thread import time class Local(object): def __init__(self): object.__setattr__(self,'storage',{}) def __setattr__(self, k, v): ident = get_ident() if ident in self.storage: self.storage[ident][k] = v else: self.storage[ident] = {k: v} def __getattr__(self, k): ident = get_ident() return self.storage[ident][k] obj = Local() def task(arg): obj.val = arg obj.xxx = arg print(obj.val) for i in range(10): t = Thread(target=task,args=(i,)) t.start()
partial偏函数
#偏函数的第二个部分(可变参数),按原有函数的参数顺序进行补充,参数将作用在原函数上,最后偏函数返回一个新函数 from functools import partial def test(a,b,c,d): return a+b+c+d tes=partial(test,1,2) # a=1,b=2 c=3,d=4 print(tes(3,4))
必须遵循传参统一方式,应用场景,传参形式比较随意简便,不必设置默认的变量值。
session源码分析
SecureCookieSessionInterface -open_session -save_session
请求上下文
请求步骤源码分析,跳转实现的生命周期。
from flask import Flask,request app=Flask(__name__) @app.route("/") def index(): print(request.form) return "ok" if __name__ == '__main__': #self,是app,app(),--->Flask对象,Flask的__call__ app.__call__ app.run() ''' 1 app.__call__ 2 wsgi_app(environ, start_response) 2.1 ctx = self.request_context(environ) 2.1.1 return RequestContext(self, environ) 这里的self是app,environ请求相关 2.1.2 return RequestContext(self, environ) 得到了RequestContext的对象,而且有request属性 2.2 2.1中的ctx就是RequestContext的对象 2.3 ctx.push()执行这个,就是RequestContext的对象的push方法 2.3.1 #执行这个,self-->ctx _request_ctx_stack.push(self) 2.3.1.1 我们发现_request_ctx_stack = LocalStack() 他的push方法的源码: def push(self, obj): rv = getattr(self._local, "stack", None) if rv is None: # self._local=>stack-->storage['线程id']['stack']=[ctx,] self._local.stack = rv = [] rv.append(obj) return rv 3在请求中获取request.form 3.1 request是LocalProxy的对象,当获取属性的时候会走__getattr__ def __getattr__(self, name): if name == "__members__": return dir(self._get_current_object()) #name-->form, #self._get_current_object()===>ctx.request,form #_get_current_object()---》self.__local() return getattr(self._get_current_object(), name) 3.1.1 self._get_current_object():源码:最终:partial(_lookup_req_object, "request") def _get_current_object(self): if not hasattr(self.__local, "__release_local__"): #local==>partial(_lookup_req_object, "request") #def __init__(self, local, name=None): # object.__setattr__(self, "_LocalProxy__local", local) #self.__local()===>local() return self.__local() try: return getattr(self.__local, self.__name__) except AttributeError: raise RuntimeError("no object bound to %s" % self.__name__) 4 partial(_lookup_req_object, "request")偏函数的源码 def _lookup_req_object(name): #name是request #ctx top = _request_ctx_stack.top if top is None: raise RuntimeError(_request_ctx_err_msg) #ctx-->request return getattr(top, name) 4.1中_request_ctx_stack.top @property def top(self): try: return self._local.stack[-1] except (AttributeError, IndexError): return None '''
截图源码跳转流程:
1:源码入口__call__>>>wsgi_app
2:查看请求方式内,所传的参数信息
3:reruest-context,内需要传的参数信息
4:倒退回来,切入点ctx.push
5:
6:
7:核心部分
8:request请求的方式
蓝图
对程序进行目录结构划分,类似书籍的目录。
不使用蓝图时,自己分文件,目录结构:
-templates -views --__init__.py -suer.py -order.py -app.py
app.py
from views import app if __name__ == "__main__" app.run()
__init__.py
from flask import Flask,request app = Flask(__name__) # 不导入这个不行 from.import account from.import order from.import user
user.py
from.import app @app.route("/user") def user(): return "user"
order.py
from.import app @app.route("order") def reder(): return "order"
创建蓝图并注册
推荐使用的两种蓝图模板
pro_flask_简单应用程序目录示例
创建蓝图
pro_flask_大型应用目录示例
注册蓝图
总结:
1、xxx = Blueprint("account",name,url_prefix="/xxx"):蓝图url前缀,表示url的前缀,表示该蓝图下的所有url都加前缀。
2、xxx = Blueprint("account",name,url_prefix="/xxx",template_folder="tpls"):给当前的蓝图单独使用,向上查找,当找不到,会找总的templates。
3、蓝图的befort_request,对当前蓝图有效。
4、大型项目,可以模拟出类似于django中的多个APP概念。
g对象
专门用来存储用户信息的对象,g的全称:global,g对象在一次请求中的所有的代码地方,都是可以使用的。
#g对象的特性: """ 1、当前请求内你设置就可以取,必须先设置,后取,当前请求可以取无限次 2、就算你当前请求,设置了,如果不取,其他请求过来,也取不到。 """
from flask import Flask,g,redirect,request app = Flask(__name__) app.debug = True # @app.before_request # def a(): # if request.path == '/': # request.name = "hello" # g.name = "hello" def set_g(): g.name = "hello" @app.route("/") def index(): set_g() return redirect("/index") @app.route("/index") def logon(): print(g.name) return "ok"
信号
Flask框架中的信号基于blinker,其主要就是让开发者可以在flask请求过程中定制一些用户行为。
安装:
pip3 install blinker
信号的使用
from flask import Flask,signals,render_template app = Flask(__name__) # 往信号中注册函数 #1给信号绑定要执行的函数 #无需管调用,因为flask,已经给我们设置调用点 def func(*args,**kwargs): print('触发型号',args,kwargs) #与该信号进行绑定 signals.request_started.connect(func) # signals.request_started.send # 触发信号: signals.request_started.send() @app.before_first_request def before_first1(*args,**kwargs): print("befor_first_request") @app.before_request def before_first3(*args,**kwargs): print("befor_request") @app.route('/',methods=['GET',"POST"]) def index(): print('视图') return "视图" if __name__ == '__main__': # app.wsgi_app app.run()
自定义信号
绑定+注册
from flask import Flask, current_app, flash, render_template from flask.signals import _signals app = Flask(import_name=__name__) # 自定义信号 xxxxx = _signals.signal('xxxxx') def func(sender,a): print(sender,a) print("我是自定义信号") # 自定义信号中注册函数 xxxxx.connect(func) @app.route("/x") def index(): # 触发信号 xxxxx.send("sb",a="1") return 'Index' if __name__ == '__main__': app.run()
flask-session
作用:将默认保存的签名cookie中的值,保存到redis/meecached/file/mysql/Mongodb等数据库。
安装
pip3 install flask-session
存和取:通过保存在cookie中的key,取出vlaue,到数据库拼接picke序列化,反解出来获取session。
使用
from flask import Flask,session from flask_session import RedisSessionInterface import redis app = Flask(__name__) app.secret_key="shgihh" conn=redis.Redis(host='127.0.0.1',port=6379) #use_signer是否对key签名 app.session_interface=RedisSessionInterface(conn,key_prefix="jack",use_signer=True,permanent=False) @app.route('/') def iindex(): session["gook"] = "jack" return "hello world" @app.route("/index") def login(): print(session["ok"]) return "full" if __name__ == '__main__': app.run()
使用2:设置配置,方便更换数据库来保存session数据。
from flask import Flask,session import redis from flask_session import Session app = Flask(__name__) app.config['SESSION_TYPE'] = 'redis' app.config['SESSION_REDIS'] =redis.Redis(host='127.0.0.1',port='6379') app.config['SESSION_KEY_PREFIX']="jason" Session(app) @app.route('/') def hello_world(): session['sb']='jason' return 'Hello World!' @app.route("/index") def index(): print(session['sb']) return "ok" if __name__ == '__main__': app.run()