flask 第三弹

今日内容简介
一:中间件(跟django 中间件完全不一样)
二:猴子补丁
三:蓝图(blueprint)
四:threading.local
五:请求上下文执行流程

今日内容详细
一:中间件(跟django 中间件完全不一样)

from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World!'
# 模拟中间件
class Md(object):
	def __init__(self,old_wsgi_app):
    self.old_wsgi_app = old_wsgi_app

def __call__(self,  environ, start_response):
    print('开始之前')
    ret = self.old_wsgi_app(environ, start_response)
    print('结束之后')
    return ret

if __name__ == '__main__':
#1我们发现当执行app.run方法的时候,最终执行run_simple,最后执行app(),也就是在执行app.__call__方法	
#2 在__call__里面,执行的是self.wsgi_app().那我们希望在执行他本身的wsgi之前做点事情。
#3 所以我们先用Md类中__init__,保存之前的wsgi,然后我们用将app.wsgi转化成Md的对象。
#4 那执行新的的app.wsgi_app,就是执行Md的__call__方法。
#把原来的wsgi_app替换为自定义的,

app.wsgi_app = Md(app.wsgi_app)
app.run()

二:猴子补丁

# 什么是猴子补丁?
# 只是一个概念,不属于任何包和模块
# 利用了python一切皆对象的理念,在程序运行过程中,动态修改方法
# 概念
# class Monkey():
#     def play(self):
#         print('猴子在玩')
#
#
# class Dog():
#     def play(self):
#         print('狗子在玩')
#
# m=Monkey()
# # m.play()
# m.play=Dog().play
#
# m.play()

# 有什么用?
# 这里有一个比较实用的例子,
# 很多用到import json,
# 后来发现ujson性能更高,
# 如果觉得把每个文件的import json改成import ujson as json成本较高,
# 或者说想测试一下ujson替换是否符合预期, 只需要在入口加上:

# 只需要在程序入口

# import json
# import ujson
#
# def monkey_patch_json():
#     json.__name__ = 'ujson'
#     json.dumps = ujson.dumps
#     json.loads = ujson.loads
# monkey_patch_json()
#
# aa=json.dumps({'name':'lqz','age':19})
# print(aa)



#协程:单线程下实现并发
# from gevent import monkey;monkey.patch_all()
# import gevent
# import time
# def eat():
#     print('eat food 1')
#     time.sleep(2)
#     print('eat food 2')

# def play():
#     print('play 1')
#     time.sleep(1)
#     print('play 2')

# g1=gevent.spawn(eat)
# g2=gevent.spawn(play)
# gevent.joinall([g1,g2])
# print('主')

三:蓝图(blueprint)

1 没有蓝图之前前,都是单文件
2 有了蓝图可以分文件,分app,之前的请求扩展还是一样用,只是在当前蓝图对象管理下的有效
3 蓝图使用
#第一步在app中注册蓝图,括号里是一个蓝图对象
app.register_blueprint(user.us)
# 第二步,在不同文件中注册路由时,直接使用蓝图对象注册,不用使用app了,避免了循环导入的问题
@account.route('/login.html', methods=['GET', "POST"])
4 中小型项目目录划分
项目名字
	-pro_flask文件夹
    -__init__.py
	-templates
    	-login.html
    -statics
    	-code.png
    -views
    	-blog.py
    	-account.py
    	-user.py
    -run.py
    
5 大型项目
项目名
	-pro_flask文件夹
    	-__init__.py
        -web
        	-__init__.py
        	-static
            -views.py
            -templates
		-admin
            -templates
            -static
            -views.py
            -__init__.py
    -run.py

四:threading.local

###############1 不用local,多线程写同一个数据,会导致错乱
# from threading import Thread
# import time
# lqz = -1
# def task(arg):
#     global lqz
#     lqz = arg
#     time.sleep(2)
#     print(lqz)
#
# for i in range(10):
#     t = Thread(target=task,args=(i,))
#     t.start()


################2 使用local对象,多线程写同一数据不会错乱,因为每个线程操作自己的数据
# from threading import Thread
# from threading import local
# import time
# from threading import get_ident
# # 特殊的对象
# lqz = local()
# # {'线程id':{value:1},'线程id':{value:2}....}
# def task(arg):
#     lqz.value = arg
#     time.sleep(2)
#     print(lqz.value)
# for i in range(10):
#     t = Thread(target=task,args=(i,))
#     t.start()


#######3自己写一个类似local的东西,函数版本
# from threading import get_ident,Thread
# import time
# storage = {}
# #{'线程id':{value:1},'线程id':{value:2}....}
# def set(k,v):
#     ident = get_ident()
#     if ident in storage:
#         storage[ident][k] = v
#     else:
#         storage[ident] = {k:v}
# def get(k):
#     ident = get_ident()
#     return storage[ident][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()



#######4自己写一个类似local的东西,面向对象版本
# 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)
#     time.sleep(1)
#     v = obj.get('val')
#
#     print(v)
# for i in range(10):
#     t = Thread(target=task,args=(i,))
#     t.start()

# #######3自己写一个类似local的东西,面向对象支持 . 取值赋值
# from threading import get_ident,Thread
# import time
# class Local(object):
#     storage = {}
#     def __setattr__(self, k, v):
#         ident = get_ident()
#         if ident in Local.storage:
#             Local.storage[ident][k] = v
#         else:
#             Local.storage[ident] = {k: v}
#     def __getattr__(self, k):
#         ident = get_ident()
#         return Local.storage[ident][k]
# obj = Local()
#
# def task(arg):
#     obj.val = arg
#     time.sleep(1)
#     print(obj.val)
# for i in range(10):
#     t = Thread(target=task,args=(i,))
#     t.start()


#######4  每次实例化得到一个local对象,用自己的字典存储
# 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
#     time.sleep(1)
#     print(obj.val)
# for i in range(10):
#     t = Thread(target=task,args=(i,))
#     t.start()

######5支持线程和协程
# 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()

五:请求上下文执行流程

请求上下文执行流程(ctx):
	-0 flask项目一启动,有6个全局变量
		-_request_ctx_stack:LocalStack对象
		-_app_ctx_stack :LocalStack对象
		-request : LocalProxy对象
		-session : LocalProxy对象
	-1 请求来了 app.__call__()---->内部执行:self.wsgi_app(environ, start_response)
	-2 wsgi_app()
		-2.1 执行:ctx = self.request_context(environ):返回一个RequestContext对象,并且封装了request(当次请求的request对象),session
		-2.2 执行: ctx.push():RequestContext对象的push方法
			-2.2.1 push方法中中间位置有:_request_ctx_stack.push(self),self是ctx对象
			-2.2.2 去_request_ctx_stack对象的类中找push方法(LocalStack中找push方法)
			-2.2.3 push方法源码:
			    def push(self, obj):
					#通过反射找self._local,在init实例化的时候生成的:self._local = Local()
					#Local()flask封装的支持线程和协程的local对象
					# 一开始取不到stack,返回None
					rv = getattr(self._local, "stack", None)
					if rv is None:
						#走到这,self._local.stack=[],rv=self._local.stack
						self._local.stack = rv = []
					# 把ctx放到了列表中
					#self._local={'线程id1':{'stack':[ctx,]},'线程id2':{'stack':[ctx,]},'线程id3':{'stack':[ctx,]}}
					rv.append(obj)
					return rv
	-3 如果在视图函数中使用request对象,比如:print(request)
		-3.1 会调用request对象的__str__方法,request类是:LocalProxy
		-3.2 LocalProxy中的__str__方法:lambda x: str(x._get_current_object())
			-3.2.1 内部执行self._get_current_object()
			-3.2.2 _get_current_object()方法的源码如下:
			    def _get_current_object(self):
					if not hasattr(self.__local, "__release_local__"):
						#self.__local()  在init的时候,实例化的,在init中:object.__setattr__(self, "_LocalProxy__local", local)
						# 用了隐藏属性
						#self.__local 实例化该类的时候传入的local(偏函数的内存地址:partial(_lookup_req_object, "request"))
						#加括号返回,就会执行偏函数,也就是执行_lookup_req_object,不需要传参数了
						#这个地方的返回值就是request对象(当此请求的request,没有乱)
						return self.__local()
					try:
						return getattr(self.__local, self.__name__)
					except AttributeError:
						raise RuntimeError("no object bound to %s" % self.__name__)
			-3.2.3 _lookup_req_object函数源码如下:
				def _lookup_req_object(name):
					#name是'request'字符串
					#top方法是把第二步中放入的ctx取出来,因为都在一个线程内,当前取到的就是当次请求的ctx对象
					top = _request_ctx_stack.top
					if top is None:
						raise RuntimeError(_request_ctx_err_msg)
					#通过反射,去ctx中把request对象返回
					return getattr(top, name)
			-3.2.4 所以:print(request) 实质上是在打印当此请求的request对象的__str__
	-4 如果在视图函数中使用request对象,比如:print(request.method):实质上是取到当次请求的reuquest对象的method属性
	
	-5 最终,请求结束执行: ctx.auto_pop(error),把ctx移除掉
	
其他的东西:
	-session:
		-请求来了opensession
			-ctx.push()---->也就是RequestContext类的push方法的最后的地方:
				if self.session is None:
					#self是ctx,ctx中有个app就是flask对象,   self.app.session_interface也就是它:SecureCookieSessionInterface()
					session_interface = self.app.session_interface
					self.session = session_interface.open_session(self.app, self.request)
					if self.session is None:
						#经过上面还是None的话,生成了个空session
						self.session = session_interface.make_null_session(self.app)
		-请求走了savesession
			-response = self.full_dispatch_request() 方法内部:执行了before_first_request,before_request,视图函数,after_request,savesession
			-self.full_dispatch_request()---->执行:self.finalize_request(rv)-----》self.process_response(response)----》最后:self.session_interface.save_session(self, ctx.session, response)
	-请求扩展相关
		before_first_request,before_request,after_request依次执行
	-flask有一个请求上下文,一个应用上下文
		-ctx:
			-是:RequestContext对象:封装了request和session
			-调用了:_request_ctx_stack.push(self)就是把:ctx放到了那个位置
		-app_ctx:
			-是:AppContext(self) 对象:封装了当前的app和g
			-调用 _app_ctx_stack.push(self) 就是把:app_ctx放到了那个位置
-g是个什么鬼?
	专门用来存储用户信息的g对象,g的全称的为global 
	g对象在一次请求中的所有的代码的地方,都是可以使用的 
	
	
-代理模式
	-request和session就是代理对象,用的就是代理模式

image

posted @ 2021-05-20 16:10  meng神  阅读(55)  评论(0)    收藏  举报