偏函数的功能很简单,以函数名为参数传入functools.partial中对函数进行改造;下面先看下例子:
import functools
def fun(name, age):
print(name, age)
new_fun = functools.partial(fun, age=19)
new_fun('andy')
# 输出: andy 19
#fun = functools.partial(fun, age=19)
# fun('andy')
可以看到,上面的例子中,偏函数的功能类似于事先传入一个参数,封装在函数内部,所以下次调用时只需要传入一个参数即可。
那如果,函数本身只有一个参数呢?
import functools
def fun(name):
print(name)
fun = functools.partial(fun, name='andy')
fun()
# 输出 andy
很明显,由于偏函数的使用,已经将参数name传入,再次调用时,便不需要传入参数了。这个在flask中有什么应用呢?
在flask的global模块中:
request = LocalProxy(partial(_lookup_req_object, "request"))
session = LocalProxy(partial(_lookup_req_object, "session"))
g = LocalProxy(partial(_lookup_app_object, "g"))
来看看_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)
这个函数只有一个name参数,top, LocalStack中的t栈顶,返回的是栈中的name键对应的值。在global模块中,传入了name=request,
也就是说事先将request, session传入了_lookup_re_object函数,经过偏函数的改造,后面再使用_lookup_req_object函数时,便不需要传入函数。
接着我们看看LocalProxy
为了说明这个原理,我们先看一人简单的例子:
class LocalProxy(object):
def __init__(self, f):
self.f = f()
def __setitem__(self, key, value):
self.f[key] = value
def __getitem__(self,item):
return self.f[item]
def __getattr__(self, item):
return getattr(self.f, item)
def func():
return ctx.session
def func2():
return ctx.request
我们暂时忽略这里的ctx,只是用来演示:
functions = LocalProxy(func)
functions['key1'] = 'andy'
functions['key2']
functions.method
我们把func当作参数传入LocalProxy, LocalProxy.func = func() = ctx.session
functions['key1']调用LocalProxy的setitem方法,LocalProxy.f[key]=ctx.session['key']='andy'
functions['key2'] 调用LocalProxy的getitem方法,即ctx.session['key2']
functions.method 则调用LocalProxy的getattr方法, getattr(ctx.request, method)
下面看看flask中的LocalProxy
class LocalProxy(object):
def __init__(self, local, name=None):
object.__setattr__(self, "_LocalProxy__local", local)
object.__setattr__(self, "__name__", name)
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__)
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]
下面一点点分析代码:
def __init__(self, local, name=None):
object.__setattr__(self, "_LocalProxy__local", local)
object.__setattr__(self, "__name__", name)
local是:from werkzeug.local import Local, 也就是一个以线程id为键的字典,它的值是也是一个字典。即:__storage__ = {id:{}}
_LocalProxy__local = {id:{}},后面调用的getitem, setitem等都是去这个字典中取值。
可以看到,它与上面的例子是一样的,只是它看起来比较绕,因为这些值是在local对象中,本质上都是去Local中设置值,获取值等操作,但LocalProxy的作用正如它的名字,代理人的角色,而不是直接去取。考虑到这里涉及的源码太多,如果后面有时间再更新。