我已经阅读了几个比较partial
与lambda
的参数,但其中大多数都谈到了partial
如何更灵活(不限于表达式(并提供有关包装函数的信息。 但我想从呼叫者的角度考虑这一点。 这是我的情况。
我有一个函数,它采用 1 参数修饰符函数。 请求被传递到要修改的修饰符函数中:
def my_func(request, modifier):
modifier(request)
我还在构建一些实用程序,可以更轻松地创建参数化修饰符函数,例如向请求添加/修改 URL 参数。 我想到了两种方法,但不确定哪种更好。
选项 1
def add_params(request, params):
for param in params:
# Manipulate the request with param.
这样,调用方可以使用functools.partial
来绑定params
,如下所示:
modifier = functools.partial(add_params, params={'abc':'123'})
选项 2
def add_params(params):
def func(request):
for param in params:
# Modify request with param.
return func
然后调用者像这样使用它:
modifier = add_params({'abc':'123'})
问题
如果我不关心函数自检,使用选项 2 有什么缺点吗? 选项 2 会遇到后期绑定问题吗?(尽管我的用例没有遇到这种情况(。 我真的很喜欢选项 2 对呼叫者来说更容易使用的方式。
从数学角度来看,这两个函数是完全同构的(尽管它们的效率可能会有所不同(:
# Option 1
(Request, Params) -> None
# Option 2
Params -> (Request -> None)
出于您的目的,我会说选项 2 提供了最大的便利,因为该功能已经是柯里式的,因此您不仅可以避免partial
,还可以轻松组合它们:
import functools
def compose(*fs):
return functools.reduce(lambda f, g: lambda x: f(g(x)), fs)
modifier = compose(add_params({'abc':'123'}),
add_params({'def':'456'}))
如果你想直接调用函数,你总是可以做:
add_params({'abc':'123'})(request)
与选项 1 相比,这并不是真正涉及的全部:
add_params(request, {'abc':'123'})
除非使用函数外部的变量,否则后期绑定应该不会造成问题,如果这样做,总有办法解决它。
不幸的是,选项 2 的缺点是定义起来很烦人,但这可以使用装饰器来简化:
def curry_request(f):
def wrapper(*args, **kwargs):
def inner(request):
f(request, *args, **kwargs)
return inner
return wrapper
@curry_request
def add_params(request, params):
# do something
def partial(func, *args, **keywords):
def newfunc(*fargs, **fkeywords):
newkeywords = keywords.copy()
newkeywords.update(fkeywords)
return func(*(args + fargs), **newkeywords)
newfunc.func = func
newfunc.args = args
newfunc.keywords = keywords
return newfunc
与部分函数实现代码相比,您的选项 2 也不错,我认为它在您的情况下没有任何缺点。但是functools.partial 是导致简化签名的常用方法,如果要将新的部分函数转换为另一个函数,仍然可以调用部分函数。如果要使用选项 2 模型,可能需要实现一个新函数