flask_day04:导出依赖pipreqs 函数和方法 threading,local对象 偏函数 wtforms

回顾

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

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

导出项目的依赖 pipreqs

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

使用第三方模块,更精准的导出依赖,pipreqs

第一步:安装 pip install pipreqs

第二步:使用命令,导出项目依赖, pipreqs ./

windows由于编码问题会出错:pipreqs ./ --encoding=utf8

mac、linux中没有问题

第三步:就会在项目根路径下生成:requirements.txt

image-20230406162318805

函数和方法

isinstance 判断一个对象,是不是一个类的对象
issubclass 判断一个类,是不是另一个类的子类

image-20230406164509689

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))

如何区分是方法还是函数?

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

函数:就是普通的函数,有几个参数就要传几个参数

方法:绑定给对象的方法,绑定给类的方法,绑定给谁的,由谁来调用,会自动把自身传入

例如:

类的绑定方法,对象可以来调用,会自动把类传入

对象的绑定方法,类可以来调用嘛?

类可以调用,但是它就是变成了普通函数,有几个值,就要传几个值,没有自动传值了

MethodType  检查一个对象,是不是方法
FunctionType 检查一个对象,是不是函数

image-20230406172003226

from type import MethodType, FunctionType

class Foo(object):
  def fetch(self):
    pass
  @classmethod
  def test(cls):
    pass
  @staticmethod
  def test1():
    pass
  
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,静态方法,就是普通函数,对象和类都可以调用,有几个值就传几个值

threading.local对象

数据错乱

image-20230406192108489

使用互斥锁解决安全并发问题,并行变串行保证了数据安全

image-20230406191931896

用local对象不会出现并发安全问题,

image-20230406194428634

local 对象是在threading里的

并发编程是,多个线程操作同一个变量,会出现并发安全问题,咱们需要加锁

使用local对象,多线程操作时,不需要加锁,不会出现数据错乱threading.local

其他语言中也有这个东西ThreadLocal,java中面试会被经常问到,而python没有人问

本质原理:

多个线程修改同一个数据,复制多份变量给每个线程用,为每个线程开辟空间进行数据存储。

每个线程操作自己的那部分数据

偏函数

可以提前传值

from functools import partial
def add(a,b,c):
  return a+b+c

# print(add(1,3,6))  # 少传了报错

现在只有一个参数,后的两参数,需要过一会才知道
借助于偏函数,先提前给他把第一个参数传入,后面知道了后面两参数,再传后面俩

add = partial(add,2)

print(add(3,4))

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

# 请求来了---》app()----->Flask.__call__--->self.wsgi_app(environ, start_response)
    def wsgi_app(self, environ, start_response):
        # environ:http请求拆成了字典
        # ctx对象:RequestContext类的对象,对象里有:当次的requets对象,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
        _request_ctx_stack.push(self)
		#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
    
    
    
 # 再往后执行,就会进入到路由匹配,执行视图函数
	# 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

自究版
切入点app.run()进去》最后有一个run_simple是werkzeug的简单服务,self是我们的app对象

run_simple(host, port, self, **options)

image
路由来了会执行app对象加括号,也就是执行Flask类的双下call

然后取下双下call干嘛了,看到了执行了这个方法

# 这个environ是请求对象,start_response开始响应对象不需要关系
def __call__(self, environ, start_response):
    return self.wsgi_app(environ, start_response)

image

def wsgi_app(self, environ, start_response):
    # environ 封装的请求对象,返回了RequestContext对象
            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:  # noqa: B001
                error = sys.exc_info()[1]
                raise
            return response(environ, start_response)
        finally:
            if self.should_ignore_error(error):
                error = None
            ctx.auto_pop(error)

去找这个方法
image
进去这个方法

def request_context(self, environ):
    # self是app对象
    return RequestContext(self, environ)

image

    def __init__(self, app, environ, request=None, session=None):
        self.app = app
        # request为空
        if request is None:
            # 走到这里去找app也就是前面传来来的app对象request_class
            # from .wrappers import Request 看到是从这个里导入的 看到的是flask自己封装的Request
            request = app.request_class(environ)
        self.request = request
        self.url_adapter = None
        try:
            self.url_adapter = app.create_url_adapter(self.request)
        except HTTPException as e:
            self.request.routing_exception = e
        self.flashes = None
        self.session = session

总结就RequestContext对象

然后继续向下走走了wsgi_app函数里面的ctx.push()就是RequestContext对象里的push

总结就RequestContext对象

然后继续向下走走了wsgi_app函数里面的ctx.push()就是RequestContext对象里的push
ctx.push()

    def push(self):
        
        
        # self 传的是ctx对象
        # _request_ctx_stack = LocalStack()是globals里面的
        _request_ctx_stack.push(self)
        
        # 拿出session
        if self.session is None:
            session_interface = self.app.session_interface
            # 看到了熟悉的open_session
            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()

image
进去看,然后去看调用的push方法

class LocalStack(object):
    # 初始化把Local()产生的对象放到到了这里面,去看这个Local类的初始化,就在上面
    def __init__(self):
        self._local = Local()

image

class Local(object):
    __slots__ = ("__storage__", "__ident_func__")
 
    def __init__(self):
        # 初始化设置__storage__空字典
        object.__setattr__(self, "__storage__", {})
        # 这个是获取id的方法内存地址也放到对象里面
        object.__setattr__(self, "__ident_func__", get_ident)
 

然后回过头来去看LocalStack类里面的push方法

    def push(self, obj):
        """Pushes a new item to the stack"""
        # obj 是ctx也就是当次请求request对象
        # 这里刚开是的线程id对应的肯定没有,所有就是往LocalStack对象的_local也就是Local对象存request对象
        rv = getattr(self._local, "stack", None)
        if rv is None:
            self._local.stack = rv = []
        rv.append(obj)
        return rv

image
去看Local类中双下setattr

    def __setattr__(self, name, value):
        # 获取线程(协程)id
        ident = self.__ident_func__()
        # 拿到这个全局对象字典
        storage = self.__storage__
        try:
            # 尝试往这个字典这个线程id对应的key对象name也就是stack,value是列表进行赋值,如果存在那么会直接覆盖
            storage[ident][name] = value
        except KeyError:
            # 不存在则像这个id赋值,一个stack key对应的是空列表
            storage[ident] = {name: value}

image

    def push(self, obj):
        """Pushes a new item to the stack"""
        # obj 是ctx也就是当次请求request对象
        # 这里刚开是的线程id对应的肯定没有,所有就是往LocalStack对象的_local也就是Local对象存request对象
        rv = getattr(self._local, "stack", None)
        if rv is None:
            self._local.stack = rv = []
        # 这里是把RequestContext对象放到了列表里面,返回了这个列表现在的数据就是
        """
        __storage__={'线程id':{'stack':[RequestContext对象]}}
        """
        rv.append(obj)
        return rv

image

紧接着回到wsgi_app函数中看这个(到这里往下就是路由匹配执行视图函数那一套了,暂时可以先不看)

# 这个里是执行视图函数,视图扩展
response = self.full_dispatch_request(

def full_dispatch_request(self):
    # 执行请求扩展中的before_first_request
    self.try_trigger_before_first_request_functions()
    try:
        request_started.send(self)
        # 这里执行的是请求扩展,以及看看是不是用的蓝图
        rv = self.preprocess_request()
        if rv is None:
            # 这里是真正的路由匹配以及执行了
            rv = self.dispatch_request()
   except Exception as e:
          rv = self.handle_user_exception(e)
  # 这里面把session加密后一并返回给了前端里面的
"""
	这个函数调用save_session()进行了加密保存以及返回了有兴趣自己可以进去看看
            response = self.process_response(response)
"""
   return self.finalize_request(rv)

image
dispatch_request

    def dispatch_request(self):
        # 拿到request对象
        req = _request_ctx_stack.top.request
        # routing_exception默认是None,所有不走
        if req.routing_exception is not None:
            self.raise_routing_exception(req)
        # werkzug的封装了一层这个就是路由例如/index或则/home/index
        rule = req.url_rule
        # if we provide automatic options for this URL and the
        # request came with the OPTIONS method, reply automatically
        # 这里是处理复杂请求头
        if (
                getattr(rule, "provide_automatic_options", False)
                and req.method == "OPTIONS"
        ):
            return self.make_default_options_response()
        # otherwise dispatch to the handler for that endpoint
        # 这里是真正执行路由匹配的用endpoint来进行匹配
        return self.view_functions[rule.endpoint](**req.view_args)

到这里一个请求就结束了

现在我们去看看在请求中使用request的情况
request = LocalProxy(partial(_lookup_req_object, "request"))

去看看初始化都有什么

class LocalProxy(object):
	# 这个是注册
    __slots__ = ("__local", "__dict__", "__name__", "__wrapped__")
 
    # 这个name是None
    def __init__(self, local, name=None):
        # 这里_LocalProxy__local是给这个类对象添加私有属性,属性名叫__local
        object.__setattr__(self, "_LocalProxy__local", local)
        object.__setattr__(self, "__name__", name)

image

# LocalProxy 代理类---》method---》代理类去当前线程的stack取出ctx,取出当时放进去的request
	视图函数中:print(request.method)

print(request) 执行LocalProxy类的__str__方法

request.method 执行LocalProxy类的_getattr_

然后去看__getattr__方法

    def __getattr__(self, name):
        if name == "__members__":
            return dir(self._get_current_object())
        # 从request对象里面拿出对象的method,以及其他属性
        return getattr(self._get_current_object(), name)

def _get_current_object(self):
        # 这里取__local对象没有__release_local__
        if not hasattr(self.__local, "__release_local__"):
            # 然后就是是partial(_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__)

执行self.__local()也就是>partial(_lookup_req_object, "request")也就是

def _lookup_req_object(name):
    top = _request_ctx_stack.top  # 这个是伪装属性
    if top is None:
        raise RuntimeError(_request_ctx_err_msg)
    # 最终这里拿到了Local对象里面的存放的ctx对象
    return getattr(top, name

    @property
    def top(self):
        try:
            # 这里是拿出线程id对象的存在Local对象stack里面的对象列表里最后的一个
            return self._local.stack[-1]
        except (AttributeError, IndexError):
            return None

Local类对象中getattr
    def __getattr__(self, name):
        try:
            # 获取线程(协程)对应的RequestContext对应【name=stack】
            return self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)

image

wtforms(了解)

Django中有forms组件:生成前端模板,校验数据,渲染错误信息

flask中使用第三方的问题forms,实现Django的forms一样的功能

第一步:导入,定义一个类,继承forms

第二步:模板中,for循环生成模板

第三步:视图函数中,使用form校验数据

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>

补充

进程:进程是资源分配的最小单位,一个应用程序运行,至少会开启一个进程
线程:线程是cpu调度的最小单位,CPU执行的的最小单位
协程:单线程下实现并发,代码层面遇到IO,自己切换

image-20230406120220912

environ就是http请求的字典对象

image-20230406120628144

posted @   小福福  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
  1. 1 原来你也在这儿 温余福
  2. 2 世间美好和你环环扣扣 温余福
  3. 3 随风起舞 温余福
  4. 4 罪恶都市 温余福
世间美好和你环环扣扣 - 温余福
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

作词 : 尹初七

作曲 : 温余福

编曲 : 彭圣杰

偏偏秉烛夜游

偏偏秉烛夜游

午夜星辰 似奔走之友

爱你每个结痂伤口

酿成的陈年烈酒

入喉尚算可口

入喉尚算可口

怎么泪水 还偶尔失守

邀你细看心中缺口

裂缝中留存 温柔

此时已莺飞草长 爱的人正在路上

此时已莺飞草长 爱的人正在路上

我知他风雨兼程 途经日暮不赏

穿越人海 只为与你相拥

此刻已皓月当空 爱的人手捧星光

我知他乘风破浪 去了黑暗一趟

感同身受 给你救赎热望

知道你不能 还要你感受

知道你不能 还要你感受

让星光加了一点彩虹

让樱花偷偷 吻你额头

让世间美好 与你环环相扣

此时已莺飞草长 爱的人正在路上

此时已莺飞草长 爱的人正在路上

我知他风雨兼程 途经日暮不赏

穿越人海 只为与你相拥

此刻已皓月当空 爱的人手捧星光

我知他乘风破浪 去了黑暗一趟

感同身受 给你救赎热望

此时已莺飞草长 爱的人正在路上

此时已莺飞草长 爱的人正在路上

我知他风雨兼程 途经日暮不赏

穿越人海 只为与你相拥

此刻已皓月当空 爱的人手捧星光

我知他乘风破浪 去了黑暗一趟

感同身受 给你救赎热望

知道你不能 还要你感受

知道你不能 还要你感受

让星光加了一点彩虹

当樱花开的纷纷扬扬

当世间美好 与你环环相扣

特别鸣谢:槿葵,我们的海报制作妹妹。

原唱:柏松

吉他:柏松

和声:柏松

录音:柏松

混音:张强

点击右上角即可分享
微信分享提示