• werzeug之LocalProxy源码


    原文链接

    链接:https://www.jianshu.com/p/3f38b777a621
    

    werzeug之LocalProxy注释

    # 源码注释
    
    """
    充当本地 werkzeug 的代理。 将所有操作转发到
    近在咫多的物体。 唯一不支持转发的操作
    是右手操作和任何类型的分配。
    
    范例用法:
    
    从 werkzeug.本地导入本地
    l = 本地()
    
    这些是代理
    请求 = l("请求")
    用户 = l("用户")
    
    从 werkzeug.本地导入本地 Stack
    _response_local = 本地堆栈()
    
    这是一个代理
    响应 = _response_local()
    
    """
    

    LocalProxy用于代理Local对象和LocalStack对象,而所谓代理就是作为中间的代理人来处理所有针对被代理对象的操作,如下图

    源码如下

    @implements_bool
    class LocalProxy(object):
    	__slots__ = ('__local', '__dict__', '__name__', '__wrapped__')
    
    	def __init__(self, local, name=None):
    		object.__setattr__(self, '_LocalProxy__local', local)
    		object.__setattr__(self, '__name__', name)
    		if callable(local) and not hasattr(local, '__release_local__'):
    			# "local" is a callable that is not an instance of Local or
    			# LocalManager: mark it as a wrapped function.
    			object.__setattr__(self, '__wrapped__', local)
    
    	def _get_current_object(self):
    		"""Return the current object.  This is useful if you want the real
    		object behind the proxy at a time for performance reasons or because
    		you want to pass the object into a different context.
    		"""
    		# 由于所有Local或LocalStack对象都有__release_local__ method, 所以如果没有该属性就表明self.__local为callable对象
    		if not hasattr(self.__local, '__release_local__'):
    			return self.__local()
    		try:
    			# 此处self.__local为Local或LocalStack对象
    			return getattr(self.__local, self.__name__)
    		except AttributeError:
    			raise RuntimeError('no object bound to %s' % self.__name__)
    
    	@property
    	def __dict__(self):
    		try:
    			return self._get_current_object().__dict__
    		except RuntimeError:
    			raise AttributeError('__dict__')
    
    	def __getattr__(self, name):
    		if name == '__members__':
    			return dir(self._get_current_object())
    		return getattr(self._get_current_object(), name)
    
    	def __setitem__(self, key, value):
    		self._get_current_object()[key] = value
    
    	def __delitem__(self, key):
    		del self._get_current_object()[key]
    
    	if PY2:
    		__getslice__ = lambda x, i, j: x._get_current_object()[i:j]
    
    		def __setslice__(self, i, j, seq):
    			self._get_current_object()[i:j] = seq
    
    		def __delslice__(self, i, j):
    			del self._get_current_object()[i:j]
    
    	# 截取部分操作符代码
    	__setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)
    	__delattr__ = lambda x, n: delattr(x._get_current_object(), n)
    	__str__ = lambda x: str(x._get_current_object())
    	__lt__ = lambda x, o: x._get_current_object() < o
    	__le__ = lambda x, o: x._get_current_object() <= o
    	__eq__ = lambda x, o: x._get_current_object() == o
    

    浅析

    1.	首先在__init__method中传递的local参数会被赋予属性_LocalProxy__local,
    该属性可以通过self.__local进行访问
    
    2. LocalProxy通过_get_current_object来获取代理的对象。需要注意的是当初始化参数
    为callable对象时,则直接调用以返回Local或LocalStack对象,具体看源代码的注释。
    
    3. 重载了绝大多数操作符,以便在调用LocalProxy的相应操作时,
    通过_get_current_object method来获取真正代理的对象,然后再进行相应操作
    

    LocalProxy的使用

    #初始化LocalProxy有三种方式:
    
    '''
    1. 通过Local或者LocalStack对象的__call__ method
    from werkzeug.local import Local
    l = Local()
    
    # these are proxies
    request = l('request')
    user = l('user')
    
    
    from werkzeug.local import LocalStack
    _response_local = LocalStack()
    
    # this is a proxy
    response = _response_local()
    
    '''
    
    上述代码直接将对象像函数一样调用,这是因为Local和LocalStack都实现了__call__ 方法,这样其对象就是callable的,因此当我们将对象作为函数调用时,
    实际调用的是__call__ 方法,可以看下本文开头部分的Local的源代码,
    会发现__call__ 方法,会返回一个LocalProxy对象
    
    2.	通过LocalProxy类进行初始化
    
    '''
    l = Local()
    request = LocalProxy(l, 'request')
    '''
    
    实际上这段代码跟第一种方式是等价的,但这种方式是最'原始'的方式,
    我们在Local的源代码实现中看到其__call__ 方法就是通过这种方式生成LocalProxy的
    
    3. 使用callable对象作为参数
    
    '''
    request = LocalProxy(get_current_request())
    '''
    通过传递一个函数,我们可以自定义如何返回Local或LocalStack对象
    

    为什么要使用LocalProxy

    可是说了这么多,为什么一定要用proxy,而不能直接调用Local或LocalStack对象呢?
    这主要是在有多个可供调用的对象的时候会出现问题,
    

    如下图

    示例:不用LocalProxy

    # use Local object directly
    from werkzeug.local import LocalStack
    user_stack = LocalStack()
    user_stack.push({'name': 'Bob'})
    user_stack.push({'name': 'John'})
    
    def get_user():
    	# do something to get User object and return it
    	return user_stack.pop()
    
    
    # 直接调用函数获取user对象
    user = get_user()
    print user['name']
    print user['name']
    
    # 结果
    '''
    John
    John
    '''
    

    示例:使用LocalProxy

    # use LocalProxy
    from werkzeug.local import LocalStack, LocalProxy
    user_stack = LocalStack()
    user_stack.push({'name': 'Bob'})
    user_stack.push({'name': 'John'})
    
    def get_user():
    	# do something to get User object and return it
    	return user_stack.pop()
    
    # 通过LocalProxy使用user对象
    user = LocalProxy(get_user)
    print user['name']
    print user['name']
    
    # 结果
    '''
    Bob
    John
    '''
    

    总结

    直接使用LocalStack对象,user一旦赋值就无法再动态更新了,而使用Proxy,
    每次调用操作符(这里[]操作符用于获取属性),都会重新获取user,
    从而实现了动态更新user的效果。
    
    Flask以及Flask的插件很多时候都需要这种动态更新的效果,
    因此LocalProxy就会非常有用了
    

    如下图

    偏函数

    Python的functools模块提供了很多有用的功能,其中一个就是偏函数(Partial function)
    偏函数的作用:将所作用的函数作为partial()函数的第一个参数,原函数的各个参
    数依次作为partial()函数的后续参数,原函数有关键字参数的一定要带上关键字,
    没有的话,按原有参数顺序进行补充。
    
    简单总结functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设
    置默认值),返回一个新的函数,调用这个新函数会更简单。
    

    flask之LocalProxy应用偏函数

    # LocalProxy代理
    import functools
    class LocalProxy(object):
    	def __init__(self, local):
    	
    		# self.__local = local
    		object.__setattr__(self, "_LocalProxy__local", local) 
    
    
    	def _get_current_object(self):
    		return self.__local() # self._LocalProxy__local()
    
    	def __setitem__(self, key, value):
    		# 函数() 自动传入session,ctx.session[key] = value
    		self._get_current_object()[key] = value
    
    	def __getattr__(self, name):
    		# ctx.request.method
    		return getattr(self._get_current_object(), name)
    

    def _lookup_req_object(name):

    """
    
    传入参数:name = request/session/g
    
    """
    
    # 取栈顶 ctx = (request, session) / app_ctx = (app, g)
    top = _request_ctx_stack.top
    if top is None:
        raise RuntimeError(_request_ctx_err_msg)
    	
    # return:ctx.request / ctx.session/ app_ctx.g 
    return getattr(top, name)
    
    
    _request_ctx_stack = LocalStack()
    _app_ctx_stack = LocalStack()
    
    # request.form >>> 触发__getattr__
    request = LocalProxy(partial(_lookup_req_object, "request"))
    
    # session['k1'] = 123,触发__setitem__
    session = LocalProxy(partial(_lookup_req_object, "session"))
    g = LocalProxy(partial(_lookup_app_object, "g"))
    希望你眼眸有星辰,心中有山海,从此以梦为马,不负韶华
  • 相关阅读:
    eclipse安装Genymotion插件
    [Eclipse插件] 安装和使用JD-Eclipse插件
    [Eclipse插件] Eclipse中如何安装和使用GrepCode插件
    [Android Studio] Android Studio如何删除module(转载)
    前端html基本标签
    python实现简单FTP
    文件的md5计算
    suprocess模块
    几个小例子
    configparser模块
  • 原文地址:https://www.cnblogs.com/daviddd/p/11938519.html
Copyright © 2020-2023  润新知