• flask框架-下


    Local与偏函数

    threasing.local

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

    不使用therading.local

    # 不用local
    from threading import Thread
    import time
    cxw = -1
    def task(arg):
        global cxw
        cxw = arg
        # time.sleep(2)
        print(cxw)
    
    for i in range(10):
        t = Thread(target=task,args=(i,))
        t.start()

    结果分析:在运行时结果全部显示为9,打印的数据是混乱,当把time.sleep删除后,结果正常打印1,2,3,4....10。

    使用threading.local

    from threading import Thread
    from threading import local
    import time
    from threading import get_ident
    # 特殊的对象
    cxw = local()
    def task(arg):
        # 对象.val = 1/2/3/4/5
        cxw.value = arg
        time.sleep(2)
        print(cxw.value)
    for i in range(10):
        t = Thread(target=task,args=(i,))
        t.start()

      使用local()时数据能正常显示,不混乱。

    通过字典自定义threaing.local(函数版)

    # 线程下 获取id
    from threading import get_ident,Thread
    import time
    storage = {}
    def set(k,v):
        index = get_ident()
        if index in storage:
            storage[index][k] = v
        else:
            storage[index] = {k:v}
    def get(k):
        index = get_ident()
        return storage[index][k]
    
    def task(arg):
        set("val",arg)
        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 = {}
        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()

    通过setattr和getattr实现

    # 反射
    # hasattr(object,name) # 判断对象是否拥有某个属性
    # setattr(object,name,value) #为对象增加新的属性
    # getattr(object,name,default) #从对象中获取某个属性
    # delattr(object,name) #从对象中删除某个属性
    #
    from threading import get_ident,Thread
    import time
    class Local(object):
        storage = {}
        def __setattr__(self, key, value):
            ident = get_ident()
            if ident in Local.storage:
                Local.storage[ident][key] = value
            else:
                Local.storage[ident] = {key:value}
        def __getattr__(self, key):
            ident = get_ident()
            return Local.storage[ident][key]
    obj = Local()
    def task(arg):
        obj.val = arg
        print(obj.val)
    for i in range(10):
        t = Thread(target=task,args=(i,))
        t.start()

    每个对象有自己的存储空间(字典)

    from threading import get_ident,Thread
    import time
    class Local(object):
        def __init__(self):
            object.__setattr__(self,'storage',{})
            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
        print(obj.val)
    for i in range(10):
        t = Thread(target=task,args=(i,))
        t.start()

    兼容线程和协程(源码到request中去看,看local的getattr,setattr)

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

      以上的几种Local 实现方法是为了推导出以下源码实现:获取get_ident,setattr和getattr方法的实现。

    源码入口:app.__call__>>>wsgi_>>>push>>>Local>>

    内部核心源码:

    partial偏函数

    #偏函数的第二个部分(可变参数),按原有函数的参数顺序进行补充,参数将作用在原函数上,最后偏函数返回一个新函数
    from functools import partial
    def test(a,b,c,d):
        return a+b+c+d
    
    tes=partial(test,1,2) # a=1,b=2 c=3,d=4
    print(tes(3,4))
    必须遵循传参统一方式,应用场景,传参形式比较随意简便,不必设置默认的变量值。

    session源码分析

    SecureCookieSessionInterface
        -open_session
        -save_session

    请求上下文

    请求步骤源码分析,跳转实现的生命周期。

    from  flask import Flask,request
    app=Flask(__name__)
    @app.route("/")
    def index():
        print(request.form)
        return "ok"
    if __name__ == '__main__':
        #self,是app,app(),--->Flask对象,Flask的__call__
        app.__call__
        app.run()
    
    '''
    1     app.__call__
    2     wsgi_app(environ, start_response) 
    2.1 ctx = self.request_context(environ)
    
        2.1.1 return RequestContext(self, environ)
            这里的self是app,environ请求相关
        2.1.2 return RequestContext(self, environ)
        得到了RequestContext的对象,而且有request属性
    2.2  2.1中的ctx就是RequestContext的对象  
    
    2.3  ctx.push()执行这个,就是RequestContext的对象的push方法
         2.3.1  #执行这个,self-->ctx
            _request_ctx_stack.push(self) 
            2.3.1.1 我们发现_request_ctx_stack = LocalStack()
            他的push方法的源码:
                    def push(self, obj):
                        rv = getattr(self._local, "stack", None)
                        if rv is None:     
                            # self._local=>stack-->storage['线程id']['stack']=[ctx,]
                            self._local.stack = rv = []
                        rv.append(obj)
                        return rv
                        
    3在请求中获取request.form
    3.1 request是LocalProxy的对象,当获取属性的时候会走__getattr__
        def __getattr__(self, name):
            if name == "__members__":
                return dir(self._get_current_object())
            #name-->form,
            #self._get_current_object()===>ctx.request,form
            #_get_current_object()---》self.__local()
            
            return getattr(self._get_current_object(), name)
            
        3.1.1 self._get_current_object():源码:最终:partial(_lookup_req_object, "request")
         def _get_current_object(self):
         
            if not hasattr(self.__local, "__release_local__"):
                    #local==>partial(_lookup_req_object, "request")
                    #def __init__(self, local, name=None):
                        # object.__setattr__(self, "_LocalProxy__local", local)
                #self.__local()===>local()
                return self.__local()
            try:
                return getattr(self.__local, self.__name__)
            except AttributeError:
                raise RuntimeError("no object bound to %s" % self.__name__)
    4 partial(_lookup_req_object, "request")偏函数的源码
        def _lookup_req_object(name):
            #name是request
            #ctx
            top = _request_ctx_stack.top
            if top is None:
                raise RuntimeError(_request_ctx_err_msg)
            #ctx-->request
            return getattr(top, name)
        4.1中_request_ctx_stack.top
           @property
            def top(self):
            
            try:
                return self._local.stack[-1]
            except (AttributeError, IndexError):
                return None
    '''

    截图源码跳转流程:

    1:源码入口__call__>>>wsgi_app

    2:查看请求方式内,所传的参数信息

    3:reruest-context,内需要传的参数信息

    4:倒退回来,切入点ctx.push

    5:

    6:

    7:核心部分

    8:request请求的方式

    蓝图

      对程序进行目录结构划分,类似书籍的目录。

    不使用蓝图时,自己分文件,目录结构:

    -templates
     -views
        --__init__.py
        -suer.py
        -order.py
    -app.py

    app.py

    from views import app
    if __name__ == "__main__"
        app.run()

    __init__.py

    from flask import Flask,request
    app = Flask(__name__)
    # 不导入这个不行
    
    from.import account
    from.import order
    from.import user

    user.py

    from.import app
    @app.route("/user")
    def user():
        return "user"

    order.py

    from.import app
    @app.route("order")
    
    def reder():
        return "order"

    创建蓝图并注册

     推荐使用的两种蓝图模板

      pro_flask_简单应用程序目录示例

    创建蓝图

    pro_flask_大型应用目录示例

     注册蓝图

     总结:

    1、xxx = Blueprint("account",name,url_prefix="/xxx"):蓝图url前缀,表示url的前缀,表示该蓝图下的所有url都加前缀。

    2、xxx = Blueprint("account",name,url_prefix="/xxx",template_folder="tpls"):给当前的蓝图单独使用,向上查找,当找不到,会找总的templates。

    3、蓝图的befort_request,对当前蓝图有效。

    4、大型项目,可以模拟出类似于django中的多个APP概念。

    g对象

      专门用来存储用户信息的对象,g的全称:global,g对象在一次请求中的所有的代码地方,都是可以使用的。

    #g对象的特性:
    """
    1、当前请求内你设置就可以取,必须先设置,后取,当前请求可以取无限次
    2、就算你当前请求,设置了,如果不取,其他请求过来,也取不到。
    
    """
    from flask import Flask,g,redirect,request
    app = Flask(__name__)
    app.debug = True
    
    # @app.before_request
    # def a():
    #     if request.path == '/':
    #         request.name =  "hello"
    #         g.name = "hello"
    
    def set_g():
        g.name = "hello"
    
    @app.route("/")
    def index():
        set_g()
        return redirect("/index")
    
    @app.route("/index")
    def logon():
        print(g.name)
        return "ok"

     信号

    Flask框架中的信号基于blinker,其主要就是让开发者可以在flask请求过程中定制一些用户行为。

    安装:

    pip3 install blinker

     信号的使用


    from
    flask import Flask,signals,render_template app = Flask(__name__) # 往信号中注册函数 #1给信号绑定要执行的函数 #无需管调用,因为flask,已经给我们设置调用点 def func(*args,**kwargs): print('触发型号',args,kwargs) #与该信号进行绑定 signals.request_started.connect(func) # signals.request_started.send # 触发信号: signals.request_started.send() @app.before_first_request def before_first1(*args,**kwargs): print("befor_first_request") @app.before_request def before_first3(*args,**kwargs): print("befor_request") @app.route('/',methods=['GET',"POST"]) def index(): print('视图') return "视图" if __name__ == '__main__': # app.wsgi_app app.run()

    自定义信号

      绑定+注册

    from flask import Flask, current_app, flash, render_template
    from flask.signals import _signals
    
    app = Flask(import_name=__name__)
    
    # 自定义信号
    xxxxx = _signals.signal('xxxxx')
    
    def func(sender,a):
        print(sender,a)
        print("我是自定义信号")
    
    # 自定义信号中注册函数
    xxxxx.connect(func)
    
    @app.route("/x")
    def index():
        # 触发信号
        xxxxx.send("sb",a="1")
        return 'Index'
    
    if __name__ == '__main__':
        app.run()

    flask-session

      作用:将默认保存的签名cookie中的值,保存到redis/meecached/file/mysql/Mongodb等数据库。

    安装

    pip3 install flask-session

    存和取:通过保存在cookie中的key,取出vlaue,到数据库拼接picke序列化,反解出来获取session。

    使用

    from flask import Flask,session
    from flask_session import RedisSessionInterface
    import redis
    app = Flask(__name__)
    app.secret_key="shgihh"
    conn=redis.Redis(host='127.0.0.1',port=6379)
    #use_signer是否对key签名
    app.session_interface=RedisSessionInterface(conn,key_prefix="jack",use_signer=True,permanent=False)
    @app.route('/')
    def iindex():
        session["gook"] = "jack"
        return "hello world"
    
    @app.route("/index")
    def login():
        print(session["ok"])
        return "full"
    
    if __name__ == '__main__':
        app.run()

    使用2:设置配置,方便更换数据库来保存session数据。

    from flask import Flask,session
    import  redis
    from flask_session import Session
    app = Flask(__name__)
    app.config['SESSION_TYPE'] = 'redis'
    app.config['SESSION_REDIS'] =redis.Redis(host='127.0.0.1',port='6379')
    app.config['SESSION_KEY_PREFIX']="jason"
    Session(app)
    
    @app.route('/')
    def hello_world():
        session['sb']='jason'
        return 'Hello World!'
    
    @app.route("/index")
    def index():
        print(session['sb'])
        return "ok"
    
    if __name__ == '__main__':
        app.run()
  • 相关阅读:
    leetcode------Rotate Array
    leetcode------Validate Binary Search Tree
    leetcode------Unique Binary Search Trees II
    [错误]集合已修改;可能无法执行枚举操作
    [转载]如何申请淘宝app_key、app_secret、SessionKey?
    [转载]JS中如何定义全局变量
    [转载]C# Double toString保留小数点方法
    jquery easyui datagrid 获取选中多行
    MongoDB { code: 18, ok: 0.0, errmsg: "auth fails" } 原因
    C# WinForm开发系列
  • 原文地址:https://www.cnblogs.com/Gaimo/p/11853040.html
Copyright © 2020-2023  润新知