zoukankan      html  css  js  c++  java
  • 11_13Local与偏函数

    一。local

      在线程的处理中,常常遇到这样的问题。当一系列线程遇到io操作的时候,就会执行其他线程,这时候就会出现数据冲突,发生数据方面的问题:

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

      这样的线程是错误的。所以为了区别所有线程,在存储数据的时候往往会加上线程号作为key,取数据的时候也会加上线程号。

    from threading import get_ident,Thread
    import time
    storage = {}
    def set(k,v):
        ident = get_ident()
        if ident in storage:
            storage[ident][k] = v
        else:
            # cxw['线程id']['value']=arg
            #storage[1][val]=arg
            #storage={1:{val:agr}}
            storage[ident] = {k:v}
    
    def get(k):
        ident = get_ident()
        #1
        #storage = {1: {val: agr}}
        return storage[ident][k]
    def task(arg):
        set('val',arg)
        time.sleep(1)
        v = get('val')
        print(v)
    
    for i in range(10):
        t = Thread(target=task,args=(i,))
        t.start()
    函数方式

      这就是local中的内部原理,但是根据类进行封装就需要使用__setattr__,__getattr__,在init中创建存储字典storage防止递归。

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

      偏函数

      偏函数就是可以定义任意函数,通过偏函数先定义一部分值,再加上另外的值就可以执行的函数,:

    from functools import partial
    def test(a,b,c,d):
        return a+b+c+d
    tes=partial(test,2,2)
    print(tes)
    print(tes(300,4))
    # functools.partial(<function test at 0x00000181D14F1EA0>, 2, 2)
    # 308

    二。请求上下文。

      当在全局调用request方法的时候,就可以从其中获取request相关的数据,是在其生成的对象被调用 的时候将request放入。流程如下:

      1.app.__call__

       2.wsgi_app(environ,start_response),将app,环境和请求等信息传入。

       3.ctx = self.request_context(environ),其中是生成一个request对象的函数。此时ctx代表的就是请求对象。

       4.ctx.push(),执行请求对象中的push方法。

       5._request_ctx_stack.push(self)

      这里的_request_ctx_stack,其实就是LocalStack(),所以在这个函数中寻找push方法

       6.push方法

       以上的push方法中需要rv,rv是从_local中获取到,而local其实就是代表线程容器的Local()

       如果没有获得stake就会在其中创建rv,并将该对象添加

      以form为例子,从request中取出该元素,流程如下。

      1.request = LocalProxy(partial(_lookup_req_object, "request"))

      request是licalproxy对象,当点方法从中取东西时,会走__getattr__

       2.getattr,会走_get_current_object

       3._get_current_object

       最终的反射获取的时__init__中的封装方法"_LocalProxy__local"

       而其中的local代表的就是LocalProxy的参数,一个偏函数。

      4.偏函数partial(_lookup_req_object, "request")

      其中的函数走的时_lookup_req_object,最后获取的元素就是top

       5.top

      top代表的就是LocalStack(),所以需要运行其中dtop方法:

       而_local就是Local(),代表线程容器,从中取出刚刚传入的request对象。

    '''
    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
        
    一些解释

    三。蓝图。

      蓝图就是一个项目的框架,可以参照django写一个,

      一个蓝图需要包函以下东西。可以将app下兴建一个pro文件,在这个文件下创建项目相关

      1.manage

      里面放启动的文件和语句,需要导入app

      2.templates 

      在其同一个文件夹下的文件都可以时从其中文件夹下导入模板。

      3.视图

      视图下需要导入app进行修饰。

      4.__init__

      在该文件夹下创建初始化文件,创建app,并导入view,让manage启动时提供view。

      可以看到,不使用蓝图创建框架会使得循环导入,可以从view层的app使用蓝图,将其循环链断开。

      在视图函数中创建蓝图:

    us=Blueprint("user",__name__)
    @us.route("/")
    def index():
        return render_template("index.html")

      这样在使用路由后,就可以调用该蓝图。

      当蓝图被创建之后,需要在app下进行注册。

    from flask import Flask
    app=Flask(__name__)
    from pro import views
    app.register_blueprint(views.us)

      蓝图中所取的名字,代表每个蓝图的头,可以调用不同蓝图中相同名字 的视图函数,只要前缀添加即可。

      所以,在蓝图中不能出现名字相同的 蓝图

      各个蓝图文件下都有自己的请求扩展周期,执行哪个蓝图就执行哪个蓝图的请求扩展。

      小型flask框架:

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from flask import Blueprint
    from flask import render_template
    from flask import request
    
    account = Blueprint('acc', __name__)
    
    
    @account.route('/login.html', methods=['GET', "POST"])
    def login():
        return render_template('login.html')
    pro_flask/views/account
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from flask import Blueprint,url_for
    
    blog = Blueprint('blog', __name__)
    
    @blog.before_request
    def a():
        print("wo我是blog 的请求扩展")
    
    
    
    @blog.route("/index")
    def index():
        print(url_for("acc.login"))
        return "ok"
    pro_flask/views/blog
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from flask import Blueprint
    
    user = Blueprint('user', __name__)
    pro_flask/views/user
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from flask import Flask
    
    app = Flask(__name__,template_folder='templates',static_folder='statics',static_url_path='/static')
    
    @app.before_request
    def a():
        print("我是app里面的befor_request")
    
    from .views.account import account
    from .views.blog import blog
    from .views.user import user
    
    app.register_blueprint(account)
    app.register_blueprint(blog)
    app.register_blueprint(user)
    pro_flask/__init__
    from pro_flask import app
    
    
    if __name__ == '__main__':
        app.run()
    run

      url_prefix代表的时这个蓝图的前缀,在前端访问该蓝图下的视图时需要加前缀来区分视图:

      大型flask框架

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from flask import Blueprint
    
    admin = Blueprint(
        'admin',
        __name__,
        template_folder='templates',
        static_folder='static'
    )
    from . import views
    pro_flask/admin/__init__
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    
    from . import admin
    
    
    @admin.route('/index')
    def index():
        return 'Admin.Index'
    pro_flask/admin/views
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from flask import Blueprint
    
    web = Blueprint(
        'web',
        __name__,
        template_folder='templates',
        static_folder='static'
    )
    from . import views
    pro_flask/web/__init__
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    
    from . import web
    
    @web.route('/index')
    def index():
        return 'Web.Index'
    pro_flask/web/views
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from flask import Flask
    from .admin import admin
    from .web import web
    
    app = Flask(__name__)
    app.debug = True
    
    app.register_blueprint(admin, url_prefix='/admin')
    app.register_blueprint(web)
    pro_flask/__init__
    from pro_flask import app
    
    if __name__ == '__main__':
        app.run()
    run

    四。g对象

      g对象的特性是,当前请求之内,可以被取出,但是需要先设置,当前请求可以无线取出,和闪现不同的是,这次请求之后,就不再有了,也就是其他请求不能取。

    from  flask import Flask,request,g
    def set_g():
        g.name='nb'
    
    @app.route("/index")
    def login():
        print(g.name)
        return "2"

      应用场景有可以再请求扩展中,添加判断,给指定路由进行传参。

    五。信号

      信号就是当到达某个阶段就会触发某个函数。

      信号需要基于blinker,所以需要安装模块:

    pip install blinker

      信号系统自带的有:

       型号的使用:

    # 往信号中注册函数
    #1给信号绑定要执行的函数
    #无需管调用,因为flask,已经给我们设置调用点
    def func(*args,**kwargs):
        print('触发型号',args,kwargs)
    #与该信号进行绑定
    signals.request_started.connect(func)
    # signals.request_started.send
    # 触发信号: signals.request_started.send()

      该信号是再请求扩展之后,view视图之前。

      自定义信号。

      仿照源码,自定义信号需要先创建一个信号对象,再进行绑定,最后使用send进行调用:

      自定义的参数默认传入一个参数,这个参数需要再函数中接受,可以不传,不传值为none,如果需要传其他参数,需要使用关键字参数。

    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

      这个模块的作用就是将原本保存cookies的值保存在redis中。

      需要安装模块:

    pip install flash-session

      应用如下:

    from flask import Flask,session
    from flask_session import RedisSessionInterface
    import redis
    app = Flask(__name__)
    app.secret_key="ajksda"
    conn=redis.Redis(host='127.0.0.1',port=6379)
    #use_signer是否对key签名
    app.session_interface=RedisSessionInterface(conn,key_prefix='jason',use_signer=True, permanent=False)
    @app.route('/')
    def hello_world():
        session['nb']='jason'
        return 'Hello World!'
    
    @app.route("/index")
    def index():
        print(session['nb'])
        return "ok"
    
    if __name__ == '__main__':
        app.run()

      首先需要调用redis创建一个数据库连接,和密钥

      将app_session_interface接口改成flask-session,其中设置一个前缀。

      保存session时,首先需要将前端返回的session中id获取,将其前缀和id作为redis的name,将其session序列化传入到redis中,并再前端设置一样的value,所以该值和redis中的值是一样的。

      当取值时,通过实现保存到key值,从redis获取到val,再反序列化获得其值。

      session的参数;

      use_signer=True,当这个值为True时,需要设置密钥,否则不需要设置密钥。

      permanent=True,当关闭浏览器时,这个session是否失效。True代表有效,False代表无效。

      简便方法:使用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()

      这个只需要传入存储方式,存储路径,和存储前缀,最后生成Session,就可以完成配置

  • 相关阅读:
    'NODE_ENV' 不是内部或外部命令,也不是可运行的程序 或批处理文件
    DOM property 和HTML attribute 的区别详解
    python语言和R语言实现机器学习算法
    特征工程二
    特征工程(一)
    梯度提升树GBD
    一种新型的聚类算法
    pcA降维 SVD
    xgboost原理
    互联网广告之工程点击率特征工程
  • 原文地址:https://www.cnblogs.com/LZXlzmmddtm/p/11853847.html
Copyright © 2011-2022 走看看