LocalProxy -- 代理实现request、session、g等

LocalProxy

LocalProxy用于代理Local对象和LocalStack对象 , 便于获取request、session、app、g等。

# 实例化了四个对象
current_app = LocalProxy(_find_app)
request = LocalProxy(partial(_lookup_req_object, "request"))
session = LocalProxy(partial(_lookup_req_object, "session"))
g = LocalProxy(partial(_lookup_app_object, "g"))
class LocalProxy(object):
    def __init__(self, local, name=None):
        object.__setattr__(self, "_LocalProxy__local", local)  

使用到的知识点:

  • 偏函数

    能够自动传递参数

    import functools
    
    def func(a1):
        print(a1)
    
    new_func = functools.partial(func,123)  # 自动传递参数
    new_func()
    
    
  • 私有成员

    class Foo:
        def __init__(self):
            self.name = 'alex'
            self.__age = 123
    
    
    obj = Foo()
    
    print(obj.name)
    print(obj._Foo__age)	# 通过_类名可以获取私有成员
    
  • getattr/setattr 的触发 -- .

    class Foo(object):
    
        def __setattr__(self, key, value):
            print(key,value)
        
        def __getattr__(self, item):
            print(item)
    
    obj = Foo()
    obj.x = 123		# 执行setattr
    obj.x			# 执行getattr
    
  • getitem/setitem的触发 -- []

    class Foo(object):
    
        def __setitem__(self, key, value):
            print(key,value)
        
        def __getitem__(self, item):
            print(item)
    
    obj = Foo()
    obj['x'] = 123		# 执行setitem
    obj['x']			# 执行getitem
    

源码解析:

  1. 当在视图函数中使用 request.时, 源码流程:

    request = LocalProxy(partial(_lookup_req_object, "request"))	# 通过偏函数,将request参数传入
    
    class LocalProxy(object):
        def __init__(self, local, name=None):
            object.__setattr__(self, "_LocalProxy__local", local)	# _LocalProxy__local=local
            #local是: _lookup_req_object函数返回值,也是一个函数,只有 函数名 + ()就会执行
    

    通过 LocalStack() 对象的top方法,获取栈顶的对象 ctx, 然后执行 ctx.request。 相当于 request=ctx.request

    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.request
    
    
    def top(self):
    
        try:
            return self._local.stack[-1]  # 获取栈顶的对象 ctx
        except (AttributeError, IndexError):
            return None
    

    当执行request.method时,会执行LocalProxy()对象的__getattr__方法

    def __getattr__(self, name):
        return getattr(self._get_current_object(), name)
    # self._LocalProxy__local()执行,(私有成员),也就是ctx.request.method
    
  2. session, 与request执行流程相似

    session = LocalProxy(partial(_lookup_req_object, "session"))	# 传入session
    
    class LocalProxy(object):
        def __init__(self, local, name=None):
            object.__setattr__(self, "_LocalProxy__local", local)	# _LocalProxy__local=local
            #local是: _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)		# 加 ()可执行
    

    当设置 session[‘k1’] = 123时, 执行__setitem__

    def __setitem__(self, key, value):
        self._get_current_object()[key] = value
    
    def _get_current_object(self):
    	return self.__local()	# self._LocalProxy__local()执行,(私有成员),也就是ctx.session['k1']=123
    
  3. current_app,返回的是app

    current_app = LocalProxy(_find_app)
    

    通过 LocalStack() 对象的top方法,获取栈顶的对象app_ctx,

    def _find_app():
        top = _app_ctx_stack.top	# 获取app_ctx对象
        if top is None:
            raise RuntimeError(_app_ctx_err_msg)
        return top.app
    
  4. g的流程相似,(略, 见下的使用)。

总结:

# session, request, current_app, g 全部都是LocalProxy对象。
"""
session['x'] = 123     ctx.session['x'] = 123
request.method         ctx.request.method
current_app.config    app_ctx.app.config
g.x1                  app_ctx.g.x1
"""

g到底是个什么鬼?

在一次请求的周期中,可以在g中设置值,那么在本次以后的请求周期中都可以读取或赋值。
相当于是一次请求周期的全局变量。 
应用:权限等
from flask import Flask,g

app = Flask(__name__,static_url_path='/xx')

@app.before_request
def f1():
    g.x1 = 123

@app.route('/index')
def index():
    print(g.x1)
    return 'hello world'

if __name__ == '__main__':
    app.run()
posted @ 2019-11-25 21:44  SensorError  阅读(164)  评论(0编辑  收藏  举报