上节回顾

# 1 蓝图
- 第一步:导入
- 第二步:实例化得到对象,可以指定static和templates
- 第三步:app中注册蓝图,注册蓝图时,可以指定前缀
- 第四步:使用蓝图,注册路由,注册请求扩展 # 2 g对象
-当次请求的全局对象,在当次请求中可以放值和取值
-跟session的区别是 # 3 flask中使用mysql数据库
-pymysql,在一个视图函数中,创建一个连接对象,操作,操作完就关闭连接
-连接对象不要用单例,可能会出现数据错乱问题 # 4 数据库连接池
-dbutils
-使用步骤:
1 第一步:导入,实例化得到一个对象, pool
2 第二步:pool做成单例,以模块导入
3 第三步:从pool中拿到一个连接 pool.connection()
3 第四步:使用连接获得游标,使用游标操作数据库 # 5 压测接口,验证是否使用了连接池 # 6 django中没有数据库连接池,可以使用第三方,也可以手写数据库连接池。

今日内容

1 请求上下文分析(源码:request原理)

1.1 导出项目的依赖

# 之前 pip freeze >requirments.txt  把当前解释器环境下的所有第三方依赖都导出来

# 使用第三方模块,更精确的导出依赖 pipreqs
第一步:安装 pip3 install pipreqs
第二步:使用命令,导出项目依赖 pipreqs ./
-win由于编码问题会出错:pipreqs ./ --encoding=utf8
-mac,linx没有问题 第三步:就会在项目根路径下生成:requirements.txt
'''
pipreqs:基于项目中的import导入生成requirements文件
'''

导出解释器的所有依赖,意味着可能导出项目不使用的无用依赖。使用pipreqs,可以实现对项目使用到的依赖精确导出。

1.2 函数和方法

# 只要会自动传值,就是方法,函数,有几个值就要传几个值,否则报错

# 函数就是普通的函数,有几个参数就要传几个参数
# 方法:绑定给对象的方法,绑定给类的方法,绑定给谁的,由谁来调用,会自动把自身传入
# 类的绑定方法,对象可以来调用,会自动把类传入
# 对象的绑定方法,类可以来调用? 类可以调用,但是它就变成了普通函数,有几个值,就要传几个值,没有自动传值了 # MethodType检查一个对象,是不是方法
# FunctionType检查一个对象,是不是函数
# isinstance 判断一个对象,是不是一个类的对象
# issubclass 判断一个类,是不是另一个类的子类 from types import MethodType, FunctionType class Foo(object):
def fetch(self):
pass @classmethod
def test(cls):
pass @staticmethod
def test1():
pass # a=Foo()
# print(isinstance(a,Foo))
# print(isinstance('a',Foo))
#
# class Foo2(Foo):
# pass
# class Foo3():
# pass
# print(issubclass(Foo2,Foo))
# print(issubclass(Foo3,Foo)) def add():
pass # 类来调用对象的绑定方法,
print(isinstance(Foo.fetch, MethodType)) # False 类来调用对象的绑定方法,该方法就变成了普通函数
obj = Foo()
print(isinstance(obj.fetch, MethodType)) # True 对象来调用自己的绑定方法,fetch就是方法
print(isinstance(Foo.fetch, FunctionType)) # True 类来调用对象的绑定方法,该方法就变成了普通函数 print(isinstance(add, FunctionType)) # True 就是个普通函数
print(isinstance(add, MethodType)) # False 就是个普通函数 print(isinstance(Foo.test, MethodType)) # True test 是绑定给类的方法,类来调用,就是方法 print(isinstance(obj.test, MethodType)) # True 对象调用类的绑定方法,还是方法 print(isinstance(Foo.test1, MethodType)) # False 是普通函数
print(isinstance(obj.test1, MethodType)) # False 是普通函数
print(isinstance(obj.test1, FunctionType)) # True,静态方法,就是普通函数,对象和类都可以调用,有几个值就传几个值 # 关于issubclass class A:
pass class B:
pass class C:
pass class D(A, B):
pass print(issubclass(D, (C,)))
print(issubclass(D, (A, C)))
print(issubclass(D, (A, C))) '''
issubclass(x, (A, B, ...)) 相当于 issubclass(x, A) or issubclass(x, B) or ...
'''

1.3 threading.local对象

# local 对象
# 并发编程时,多个线程操作同一个变量,会出现并发安全的问题,咱们需要加锁
# 使用local对象,多线程并发操作时,不需要加锁,不会出现数据错乱threading.local
# 其他语言中也有这个东西ThreadLocal,java中面试会被经常问到,python没人问 # 本质原理:
多个线程修改同一个数据,复制多份变量给每个线程用,为每个线程开辟一块空间进行数据存储
每个线程操作自己的那部分数据

flask的request对象,就是重写了一个local类,代码示例:

# # 数据错乱
# # 原因是io操作导致立切换到下一个线程,一直切换到最后一个线程,将全局变量赋值为9
# import time
# from threading import get_ident, Thread
#
# '''get_ident C代码
# thread_get_ident(PyObject *self, PyObject *Py_UNUSED(ignored))
# {
# unsigned long ident = PyThread_get_thread_ident();
# if (ident == PYTHREAD_INVALID_THREAD_ID) {
# PyErr_SetString(ThreadError, "no current thread ident");
# return NULL;
# }
# return PyLong_FromUnsignedLong(ident);
# }
# '''
#
# num = 0
#
#
# def task(args):
# global num
# num = args
# time.sleep(1) # 添加io操作
# print(f'当前的线程号:{get_ident()},num={num}')
#
#
# if __name__ == '__main__':
# for i in range(10):
# t = Thread(target=task, args=(i,))
# t.start() # # 使用锁解决并发安全问题
# import time
# from threading import Thread, get_ident, Lock
#
# l = Lock()
#
# num = 0
#
#
# def task(args):
# print(1000) # 这行代码是并发执行的
# global num
# l.acquire() # 后面的代码是同步执行的
# num = args
# time.sleep(1)
# print(f'当前线程号:{get_ident()},num={num}')
# l.release()
#
#
# if __name__ == '__main__':
# for i in range(10):
# t = Thread(target=task, args=(i,))
# t.start() # # 使用threading.local对象 不会出现并发安全问题
# import time
# from threading import local, get_ident, Thread
#
# nb_obj = local()
# nb_obj.num = 0
#
#
# def task(args):
# nb_obj.num = args
# # time.sleep(1)
# print(f'线程号:{get_ident()},num={nb_obj.num}')
#
#
# if __name__ == '__main__':
# for i in range(10):
# t = Thread(target=task, args=(i,))
# t.start() # threading对象原理
import time try: # 对协程的支持
from greenlet import getcurrent as get_ident
except Exception as e:
from threading import get_ident, Thread class Local:
def __init__(self):
object.__setattr__(self, 'storage', {})
# self.storage = {} 会导致__setattr__一直调用自己 --> 递归 def __setattr__(self, key, value): # obj.key = value
ident = get_ident()
if ident in self.storage: # 如果这个线程使用过local对象
self.storage[ident][key] = value # 这个线程只能修改自己的 {线程1:{key:value},线程2:{key:value}}
else: # 如果线程没有使用过local对象
self.storage[ident] = {key: value} def __getattr__(self, key):
ident = get_ident()
print(self.storage)
return self.storage[ident][key] # 根据线程号去自己的字段里面取数据 obj = Local()
obj.num = 1 def task(args):
obj.num = args
time.sleep(1)
print(f'线程号:{get_ident()},num={obj.num}') if __name__ == '__main__':
for i in range(10):
t = Thread(target=task, args=(i,))
t.start()

1.4 偏函数



# 可以提前传值

from functools import partial
def add(a,b,c):
print(a)
print(b)
print(c)
return a+b+c # print(add(2,3,4)) # 传少了报错 # 可以先传入一个参数 后续再传入其他参数
# 借助于偏函数,先提前给他把第一个参数传入,后面知道了后面俩参数,再传后面俩 add=partial(add,2)
#
# # 干了很多事
#
print(add(3,4))

1.5 flask 整个生命执行流程(1.1.4版本为例)

# 本质是去字典里面,拿不同线程stack对应的ctx中的request。
from flask import Flask, request app = Flask(__name__) @app.route('/')
def index():
return 'xxx' if __name__ == '__main__':
app.run()
# 内部调用了werkzeug 的 run_simple('localhost', 4000, hello),请求如果来了,会执行hello()
# run_simple('localhost', 4000, hello)
# run_simple可以启动一个符合WSGI协议的服务器。
# run_simple(host, port, self) # 请求来了,会执行 self(),self 就是app对象,app()
# 在run_simple中会调用我们传入的self对象,这一行为会触发Flask类的__call__ # 请求来了---》app()----->Flask.__call__--->self.wsgi_app(environ, start_response)
def wsgi_app(self, environ, start_response):
# environ:http请求拆成了字典
# ctx对象:RequestContext类的对象,对象里有:当次的request对象,app对象,session对象
ctx = self.request_context(environ)
error = None
try:
try:
#ctx RequestContext类 push方法
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) # RequestContext :ctx.push
def push(self):
# _request_ctx_stack = LocalStack() ---》push(ctx对象)--》ctx:request,session,app --》传入LocalStack()对象
_request_ctx_stack.push(self) '''
_request_ctx_stack.push(self)
相当于:LocalStack().push('ctx对象')
说明:对象调用方法,会把自己传进去,所以push方法第一个参数是LocalStack对象,第二次参数是ctx(RequestContext类的对象)
''' # session相关的
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() # LocalStack() push --->obj 是ctx对象
def push(self, obj):
#self._local _local 就是咱们刚刚自己写的Local的对象---》LocalStack的init初始化的_local---》self._local = Local()---》Local对象可以根据线程协程区分数据
rv = getattr(self._local, "stack", None)
# 一开始没有值
if rv is None:
rv = []
self._local.stack = rv # self._local.stack 根据不同线程用的是自己的数据
rv.append(obj) # self._local.stack.append(obj)
# {'线程id号':{stack:[ctx]},'线程id号2':{stack:[ctx]}}
return rv '''
每个线程都有自己的Local对象,它们之间不会相互干扰。所以Local对象不是单例的,它是线程局部的。
''' # from flask import request
# 再往后执行,就会进入到路由匹配,执行视图函数
# request = LocalProxy(partial(_lookup_req_object, "request"))
# LocalProxy 代理类---》method---》代理类去当前线程的stack取出ctx,取出当时放进去的request
视图函数中:print(request.method) # print(request) 执行LocalProxy类的__str__方法
# request.method 执行LocalProxy类的__getattr__
def __getattr__(self, name): #name 是method
# self._get_current_object() 就是当次请求的request
return getattr(self._get_current_object(), name)
# 通过反射获取请求对象中的方法 # LocalProxy类的方法_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__) # self.__local 是在 LocalProxy 类实例化的时候传入的local # 在这里实例化的:request = LocalProxy(partial(_lookup_req_object, "request"))
# local 是 partial(_lookup_req_object, "request") #_lookup_req_object ,name=request
def _lookup_req_object(name):
top = _request_ctx_stack.top # 取出了ctx,是当前线程的ctx
if top is None:
raise RuntimeError(_request_ctx_err_msg)
return getattr(top, name) #从ctx中反射出request,当次请求的request
请求上下文执行流程(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):
#通过反射找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就是代理对象,用的就是代理模式

2 wtforms(了解)

# django 有forms组件
- 生成前端模板
- 校验数据
- 渲染错误信息 # flask 中使用第三方的wtforms 实现像django的forms一样的功能
-第一步:导入,定义一个类,继承forms
-第二步:模板中, for循环生成模板
-第三步:视图函数中,使用form校验数据
-wtforms是前后端结合使用的。前后端分离校验数据会使用别的第三方模块。 # py代码
from flask import Flask, render_template, request, redirect
from wtforms import Form
from wtforms.fields import simple
from wtforms import validators
from wtforms import widgets app = Flask(__name__, template_folder='templates') app.debug = True class LoginForm(Form):
# 字段(内部包含正则表达式)
name = simple.StringField(
label='用户名',
validators=[
validators.DataRequired(message='用户名不能为空.'),
validators.Length(min=6, max=18, message='用户名长度必须大于%(min)d且小于%(max)d')
],
widget=widgets.TextInput(), # 页面上显示的插件
render_kw={'class': 'form-control'} )
# 字段(内部包含正则表达式)
pwd = simple.PasswordField(
label='密码',
validators=[
validators.DataRequired(message='密码不能为空.'),
validators.Length(min=8, message='用户名长度必须大于%(min)d'),
validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,}",
message='密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符') ],
widget=widgets.PasswordInput(),
render_kw={'class': 'form-control'}
) @app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
form = LoginForm()
return render_template('login.html', form=form)
else:
form = LoginForm(formdata=request.form)
if form.validate():
print('用户提交数据通过格式验证,提交的值为:', form.data)
else:
print(form.errors)
return render_template('login.html', form=form) if __name__ == '__main__':
app.run() # html代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>登录</h1>
<form method="post" novalidate>
<p>{{form.name.label}}: {{form.name}} {{form.name.errors[0] }}</p> <p>{{form.pwd.label}} {{form.pwd}} {{form.pwd.errors[0] }}</p>
<input type="submit" value="提交">
</form>
</body>
</html>
# 1 为什么有了gil锁还要互斥锁
# 2 进程,线程和协程
代码如何实现
你在哪里用过
# 3 什么是鸭子类型
# 看flask 源码---》 1.1.4 版本

补充

# 进程:进程是资源分配的最小单位,一个应用程序运行,至少会开启一个进程

# 线程:线程是cpu调度的最小单位,cpu执行的最小单位

# 协程:单线程下实现并发,代码层面遇到io,自己切换

【flask】flask请求上下文分析 threading.local对象 偏函数 flask1.1.4生命执行流程 wtforms的更多相关文章

  1. flask上下文管理之threading.local

    Flask之上下文管理 知识储备之问题情境: request中的参数: 单进程单线程 单进程多线程-->reqeust 会因为多个请求,数据发生错乱.--->可以基于threading.l ...

  2. Flask补充--threading.local对象

    目录 Local 局部变量 全局变量 使用threading.local() 自定义threading.local 函数版 面向对象版 通过setattr和getattr实现 每个对象有自己的存储空间 ...

  3. flask上下文管理相关 - threading.local 以及原理剖析

    threading.local 面向对象相关: setattr/getattr class Foo(object): pass obj = Foo() obj.x1 = 123 # object.__ ...

  4. python 全栈开发,Day139(websocket原理,flask之请求上下文)

    昨日内容回顾 flask和django对比 flask和django本质是一样的,都是web框架. 但是django自带了一些组件,flask虽然自带的组件比较少,但是它有很多的第三方插件. 那么在什 ...

  5. threading.local学习

    多线程抢占问题 import time import threading obj = 5 def task(arg): global obj obj = arg time.sleep(1) print ...

  6. flask之请求与响应、闪现(阅后即焚)、请求扩展(before,after)、中间件、LOCAL对象、偏函数、

    目录 1.flask请求与响应 2.闪现 3.请求扩展 4.中间件 5.LOCAL对象 6.偏函数 templates 1.flask请求与响应 from flask import Flask,req ...

  7. flask 源码专题(十一):LocalStack和Local对象实现栈的管理

    目录 04 LocalStack和Local对象实现栈的管理 1.源码入口 1. flask源码关于local的实现 2. flask源码关于localstack的实现 3. 总结 04 LocalS ...

  8. 04 flask源码剖析之LocalStack和Local对象实现栈的管理

    04 LocalStack和Local对象实现栈的管理 目录 04 LocalStack和Local对象实现栈的管理 1.源码入口 1. flask源码关于local的实现 2. flask源码关于l ...

  9. 线程锁、threading.local(flask源码中用的到)、线程池、生产者消费者模型

    一.线程锁 线程安全,多线程操作时,内部会让所有线程排队处理.如:list/dict/Queue 线程不安全 + 人(锁) => 排队处理 1.RLock/Lock:一次放一个 a.创建10个线 ...

  10. flask框架(七)——蓝图、请求上下文、g对象、信号、flask_session

    蓝图 作用:对程序进行目录结构划分 不使用蓝图情况下,自己分文件 目录结构: -templates -views -__init__.py -user.py -order.py -app.py app ...

随机推荐

  1. python 数据可视化:直方图、核密度估计图、箱线图、累积分布函数图

    本文使用数据来源自2023年数学建模国赛C题,以附件1.附件2数据为基础,通过excel的数据透视表等功能重新汇总了一份新的数据表,从中截取了一部分数据为例用于绘制图表.绘制的图表包括一维直方图.一维 ...

  2. Flask 运用Xterm实现交互终端

    Xterm是一个基于X Window System的终端仿真器(Terminal Emulator).Xterm最初由MIT开发,它允许用户在X Window环境下运行文本终端程序.Xterm提供了一 ...

  3. 【uniapp】【外包杯】学习笔记day04 | 学习模板+vue相关知识+环境搭建

    没啥好说的,人与人的悲欢并不相同,我只觉得吵闹. 好烦啊,虽然不应该总说一些低气压的话,不过目前预见的就是有很多工作要做,并且对于完成的希望也有点没有,就这样吧,没啥好说的. 昨天做了python的作 ...

  4. 0x00.常用名词、文件下载、反弹shell

    下载文章 方法一:下载谷歌插件fireshot,捕捉整个页面 方法二:使用js代码 f12进入控制台,粘贴如下代码 (function(){ $("#side").remove() ...

  5. ELT安装

    前言: ETL是将业务系统的数据经过抽取.清洗转换之后加载到数据仓库的过程, 目的是将企业中的分散.零乱.标准不统一的数据整合到一起,为企业的决策提供分析依据, ETL是BI(商业智能)项目重要的一个 ...

  6. 安装华企盾DSC防泄密软件造成CAD2012卡住怎么办?

    将下图目录的.exe程序删除或者重命名

  7. Kernel Memory 入门系列:文档的管理

    Kernel Memory 入门系列: 文档的管理 在Quick Start中我们了解到如何快速直接地上传文档.当时实际中,往往会面临更多的问题,例如文档如何更新,如何划定查询范围等等.这里我们将详细 ...

  8. 红日靶场2-wp

    红日靶场2 环境搭建 靶场配置 靶场拓扑图如下: 首先先新建一个网卡, PC PC端虚拟机相当于网关服务器,所以需要两张网卡,一个用来向外网提供web服务,一个是通向内网. 由于作者默认的网段设置为1 ...

  9. springsecurity 使用浅谈(一)

    1. 背景 springsecurity框架主要用于Web应用的认证和授权.所谓认证就是验证当前访问系统的是不是本系统的用户,并且要确认具体是哪个用户.而授权就是经过认证后判断当前用户是否有权 限进行 ...

  10. 扩展 jQurey.i18n.properties 的能力来向 vue-i18n 靠齐

    jQuery.i18n.properties 是 jQuery 老项目的国际化框架,其实国际化方案本质上都大同小异,都是需要用翻译函数包裹词条,然后根据词条文件来进行翻译 就是使用上与其他框架不太一样 ...