flask之中间件、蓝图、请求上下文等相关内容-142

1 中间件

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替换为自定义的,
   aaa=Md(app.wsgi_app)
   app.wsgi_app = aaa
   app.run()
   # flask请求来了会执行,执行app()--->触发:类的__call__
   # app.__call__---->self.wsgi_app(environ, start_response)
   # 请求来了以后,会执行Md对象的__call__
   # aaa(environ, start_response)--->Md类的__call__()

 

2 蓝图

1 是一个类的对象,用来划分项目目录,为了避免使用app划分目录的时候出现循环导入问题
2 使用步骤
-实例化得到一个蓝图对象
       from flask import Blueprint
       account = Blueprint('account', __name__)
   -像使用app一样使用蓝图,注册路由
  @account.route('/login.html', methods=['GET', "POST"])
       def login():
           return render_template('login.html')
       
   -在app中注册蓝图
  from .views.account import account
  app.register_blueprint(account)
       
3 注意点:
1 蓝图注册进app时,指定前缀(类似于路由分发)
  app.register_blueprint(admin, url_prefix='/admin')
   2 蓝图对象在实例化的时候,可以指定当前蓝图使用哪个模板文件夹和静态文件夹
   如果不指定,默认用app的,查找顺序是先从自己里面找,找不到再找app的
  web = Blueprint('web',__name__,template_folder='templates',static_folder='static')
       
   3 蓝图对象有自己的请求扩展,请求扩展只属于当前蓝图
   # 只有访问当前蓝图管理的路径才会触发请求扩展
   web = Blueprint('web', __name__,template_folder='templates',static_folder='static')
   @web.before_request
   def web_request():
       print('web request')
   @web.after_request
   def web_after(response):
       print(response)
       return response

3 flask请求上下文

1 当flask启动,等待客户端请求
2 一旦请求来了:app()--->app.__call__()---->app.wsgi_app()
3 wsgi_app()源码如下:
   # environ:http请求字典
   # 返回一个ctx=RequestContext,对象中有Request对象,Session对象。。。
   ctx = self.request_context(environ)
   error = None
   try:
       try:
               # 把ctx对象放到了全局变量_request_ctx_stack中
               # _request_ctx_stack全局变量是LocalStack的对象
               # LocalStack类的push方法
               '''
                      def push(self, obj):
                          rv = getattr(self._local, "stack", None)
                          if rv is None:
                              self._local.stack = rv = []
                          rv.append(obj)
                          return rvbefore_first_request,before_request,after_request
                      '''
               ctx.push()
               # 执行请求扩展的东西(before_first_request,before_request,after_request)
               # 根据路径执行视图函数
               response = self.full_dispatch_request()
           except Exception as e:
               error = e
               response = self.handle_exception(e)
               except:
                   error = sys.exc_info()[1]
                   raise
                   return response(environ, start_response)
           finally:
                if self.should_ignore_error(error):
                     error = None
                     ctx.auto_pop(error)



 

 

 

 

补充

1 requirements.txt

1 在虚拟环境中直接导出
pip3 freeze >requirements.txt
2 系统环境装了很多模块,只导出当前项目依赖的模块
-pip3 install pipreqs
   -pipreqs ./ --encoding=utf8

2 方法和函数的区别

from types import MethodType,FunctionType

class Foo(object):
def fetch(self):
pass

# print(isinstance(Foo.fetch,MethodType))
# print(isinstance(Foo.fetch,FunctionType)) # True

obj = Foo()
print(isinstance(obj.fetch,MethodType)) # True
print(isinstance(obj.fetch,FunctionType))

3 threading.local


# 不用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()


# 使用local
# from threading import Thread
from threading import local
# import time
# from threading import get_ident
# # 特殊的对象,不同线程操作它,操作的是线程自己的,不会出现并发安全问题
# lqz = local()
# def task(arg):
#     # 对象.val = 1/2/3/4/5
#     lqz.value = arg
#     time.sleep(2)
#     print(lqz.value)
# for i in range(10):
#     t = Thread(target=task,args=(i,))
#     t.start()



# 自己写个local对象(low版本)

# from threading import get_ident,Thread
# import time
# storage = {}
# # {线程id号1:{key:value},线程id号2:{key:value},线程id号3:{key:value}}
# def set(k,v):
#     ident = get_ident() # 获取线程id号
#     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)
#     time.sleep(2)
#     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 = {}
#     # {线程id号1: {key: value}, 线程id号2: {key: value}, 线程id号3: {key: value}}
#     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()


# 面向对象版本,通过.取值,赋值
# from threading import get_ident,Thread
# import time
# class Local(object):
#     storage = {}
#     ## {线程id号1: {key: value}, 线程id号2: {key: value}, 线程id号3: {key: value}}
#     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()
# obj2 = Local() # 所有的local对象用的是同一个字典,不好
# def task(arg):
#     obj.val = arg
#     print(obj.val)
# for i in range(10):
#     t = Thread(target=task,args=(i,))
#     t.start()


# 不同local对象,使用自己的字典
# from threading import get_ident,Thread
# import time
# class Local(object):
#     def __init__(self):
#         # self.storage={} 递归
#         # setattr(self,'storage',{}) 递归
#         #Local.__setattr__(self,'storage',{})
#
#         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
#     time.sleep(1)
#     print(obj.val)
# for i in range(10):
#     t = Thread(target=task,args=(i,))
#     t.start()


# 多线程下可以使用,一旦加入协程,就不行了
# 自定义一个兼容线程和协程的local对象
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}

       # try:
       #     storage[ident][name] = value
       # except KeyError:
       #     storage[ident] = {name: value}


   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.storage)
   print(obj.val)
for i in range(10):
   t = Thread(target=task,args=(i,))
   t.start()

4 偏函数

from functools import partial  # 偏函数


def add(a,b,c):
   return a+b+c

add=partial(add,2) # add是一个偏函数了,提前传值

print(add)
# print(add(3,4))
print(add(2,3,4))
# print(add(2,3,4))
posted @ 2021-03-02 20:06  投降输一半!  阅读(116)  评论(0编辑  收藏  举报