让呼叫者使用functools.partial或调用工厂函数?

时间:2014-12-16 00:40:39

标签: python

我已经阅读了几个比较partiallambda的论点,但大多数人都讨论了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对于呼叫者来说更容易使用。

2 个答案:

答案 0 :(得分:1)

这两个函数从数学角度看彼此完全同构(虽然它们的效率可能会有所不同):

# 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

答案 1 :(得分:1)

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模型,你可能需要实现一个新函数