性质:在整个请求过程的前后定制一些个性化功能
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World!'
# 模拟中间件
class MyMiddle(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__':
app.run()
# 1 我们发现当执行app.run方法的时候,最终执行run_simple,最后执行app(),也就是在执行app.__call__方法
run_simple(host, port, self, **options)
# werkzeug
from werkzeug.wrappers import Request, Response
@Request.application
def hello(request):
return Response('Hello World!')
if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('localhost', 4000, hello)
# 可以看出run_simple会调用hello()执行,也就是说上面会调用self也就是app()执行
# 2 在flask类的__call__方法中
return self.wsgi_app(environ, start_response)
实现中间件需要重写app.wsgi_app,在里面加点其他功能
# 3 app.wsgi_app()=对象()=重写类的__call__()方法,基于这思想自定义了MyMiddle类
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的__init__中注册蓝图
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
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 在虚拟环境中直接导出
pip3 freeze >requirements.txt
2 系统环境装了很多模块,只导出当前项目依赖的模块
-pip3 install pipreqs
-pipreqs ./ --encoding=utf8 (windows下要写--encoding=utf8)
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))
# 对象调用是方法
# 扩展:
对象的绑定方法: 对象调用,会自动传参
类调用,是普通函数,需要手动传参
类的绑定方法(使用装饰器@classmethod):对象和类都能调用,并且都会自动传入这个类
解决并发安全的问题,多条线程操作同一个变量,会出现数据安全问题,解决该问题,需要加锁。使用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 Thread,get_ident
import time
class Local(object):
# storage = {}
# {线程id号1: {key: value}, 线程id号2: {key: value}, 线程id号3: {key: value}}
def __init__(self):
# self.storage = {} 递归
# 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()
obj2 = Local()
def task(arg):
obj.key = arg
obj2.xx = arg
time.sleep(2)
v = obj.key
v2 = obj2.xx
print(v)
print(v2)
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()
from functools import partial # 偏函数
def add(a,b,c):
return a+b+c
add=partial(add,2) # add是一个偏函数了,提前传值
print(add)
# print(add(3,4)) 9
print(add(2,3,4)) # 报错,因为传了4个值