内容概览
- 多app应用
- flask-script
- 导出项目依赖
- 函数和方法
- 偏函数
- threading.local
- 自定义local支持线程和协程
- flask请求上下文分析
多app应用
"""之前写flask,都是实例化得到一个app对象;其实还可以使用多个app对象"""
from flask import Flask
from werkzeug.serving import run_simple
from werkzeug.middleware.dispatcher import DispatcherMiddleware
app01 = Flask('app01')
app02 = Flask('app02')
@app01.route('/')
def index():
return 'app01'
@app02.route('/')
def index2():
return 'app02'
dm = DispatcherMiddleware(app01, {'/sec': app02})
if __name__ == '__main__':
run_simple('localhost', 5000, dm)
'''
1 请求来了,会执行dm(environ,start_response)
2 dm的__call__ 根据请求的地址,拿到不同的app,执行app(environ,start_response)--->Flask的__call__
'''
flask-script
"""flask的一个第三方插件;实现像django的python manage.py runserver命令操作"""
pip install flask-script==2.0.3
from flask import Flask
from flask_script import Manager
app = Flask(__name__)
manager = Manager(app)
@manager.command
def custom(arg):
"""
自定义命令
python manage.py custom 123
"""
print(arg)
@manager.option('-n', '--name', dest='name')
@manager.option('-u', '--url', dest='url')
def cmd(name, url):
"""
自定义命令(-n也可以写成--name)
执行: python manage.py cmd -n lqz -u http://www.oldboyedu.com
执行: python manage.py cmd --name lqz --url http://www.oldboyedu.com
"""
print(name, url)
@app.route('/')
def index():
return 'index'
if __name__ == '__main__':
manager.run()
"""
django中自定义命令
-第一步:在app中新建包:management
-第二步:在management下新建包:commands
-第三步:commands新建py文件,py文件名就是命令名 init.py
-第四步:init.py写入
from django.core.management.base import BaseCommand, CommandError
class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument('--name', type=str) # 指令接受的参数列表,参数名左边有两个减号,可以添加类型限制
def handle(self, *args, **options):
name = options['name']
# 执行这个命令的逻辑是什么
# python manage.py init --name=lqz
"""
导出项目依赖
"""
之前学过使用pip freeze > requirements.txt导出依赖
如果使用真实环境导出,会把所有模块都导出,可能会有问题
使用第三方模块只导出项目依赖
"""
pip install pipreqs
pipreqs ./ ==encoding=utf8
pip install requirements.txt
函数和方法
"""
函数就是普通函数,需要多少值就要传多少值
方法是绑定给对象或类,会自动传值
"""
def add(a, b):
return a + b
class Person:
def speak(self):
print('人说话')
@classmethod
def test(cls):
print('类的绑定方法')
@staticmethod
def ttt():
print('static')
p = Person()
from types import MethodType, FunctionType
print(isinstance(add, FunctionType))
print(isinstance(add, MethodType))
print(isinstance(p.speak, FunctionType))
print(isinstance(p.speak, MethodType))
print(isinstance(Person.speak, FunctionType))
print(isinstance(Person.speak, MethodType))
Person.speak(p)
print(isinstance(Person.test, FunctionType))
print(isinstance(Person.test, MethodType))
print(isinstance(p.test, FunctionType))
print(isinstance(p.test, MethodType))
print(isinstance(p.ttt, FunctionType))
print(isinstance(p.ttt, MethodType))
"""总结:只要能自动传值,就是方法,有几个值传几个值就是函数"""
偏函数
"""python内置了一个偏函数,可以将函数包裹一下,提前传参"""
from functools import partial
def add(a, b, c):
return a + b + c
print(add(1, 2, 3))
res = partial(add, 1, 2)
print(res)
print(res(3))
threading.local
"""
当使用多线程操作同一个变量,如果不加锁,可能会出现数据错乱问题
但是多个线程同时操作threading.local对象,就不会出现数据错乱
-Java:ThreadLocal
-Python:threading.local
作用:
线程变量,意思是threading.local中填充的变量属于当前线程,该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量;threading.local为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量
"""
import time
from threading import Thread, Lock
lock = Lock()
n = -1
def task(arg):
global n
n = arg
time.sleep(0.1)
print(n)
for i in range(10):
t = Thread(target=task, args=(i,))
t.start()
import time
from threading import Thread, local, get_ident
l = local()
def task(arg):
l.value = arg
time.sleep(0.1)
print(f'线程id:{get_ident()} 值:{l.value}')
for i in range(10):
t = Thread(target=task, args=(i,))
t.start()
自定义local支持线程和协程
-变量对其他线程而言是隔离的
-local: {'线程id号':{}}
-设置值:
-线程1:local.val='lqz' ---> {'线程1id号':{val:lqz},}
-线程2:local.val='pyy' ---> {'线程1id号':{val:lqz},'线程2id号':{val:pyy},}
-取值:
-线程1:print(local.val) ---->l={'线程1id号':{val:lqz},'线程2id号':{val:pyy},}--》先当前线程的id号:get_ident() l[get_ident(线程1)]['val']
-线程2:print(local.val) ---->l={'线程1id号':{val:lqz},'线程2id号':{val:pyy},}--》先当前线程的id号:get_ident() l[get_ident(线程2)]['val']
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
v = obj.val
time.sleep(0.01)
print(v)
for i in range(10):
t = Thread(target=task, args=(i,))
t.start()
flask自定义的local,支持线程和协程
class Local(object):
def __init__(self):
object.__setattr__(self, "__storage__", {})
object.__setattr__(self, "__ident_func__", get_ident)
def __getattr__(self, name):
try:
return self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name)
def __setattr__(self, name, value):
ident = self.__ident_func__()
storage = self.__storage__
try:
storage[ident][name] = value
except KeyError:
storage[ident] = {name: value}
flask请求上下文分析
def wsgi_app(self, environ, start_response):
ctx = self.request_context(environ)
error = None
try:
try:
ctx.push()
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)
def push(self):
_request_ctx_stack.push(self)
if self.session is None:
session_interface = self.app.session_interface
self.session = session_interface.open_session(self.app, self.request)
if self.session is None:
self.session = session_interface.make_null_session(self.app)
if self.url_adapter is not None:
self.match_request()
def push(self, obj):
rv = getattr(self._local, "stack", None)
if rv is None:
rv = []
self._local.stack = rv
rv.append(obj)
return rv
视图函数中:print(request.method)
def __getattr__(self, name):
return getattr(self._get_current_object(), name)
def _get_current_object(self):
if not hasattr(self.__local, "__release_local__"):
return self.__local()
try:
return getattr(self.__local, self.__name__)
except AttributeError:
raise RuntimeError("no object bound to %s" % self.__name__)
def _lookup_req_object(name):
top = _request_ctx_stack.top
if top is None:
raise RuntimeError(_request_ctx_err_msg)
return getattr(top, name)
请求上下文执行流程(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,flash,当前app对象
-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):
rv = getattr(self._local, "stack", None)
if rv is None:
self._local.stack = rv = []
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__"):
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):
top = _request_ctx_stack.top
if top is None:
raise RuntimeError(_request_ctx_err_msg)
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:
session_interface = self.app.session_interface
self.session = session_interface.open_session(self.app, self.request)
if self.session is None:
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就是代理对象,用的就是代理模式