大致相当于偏的代码

时间:2018-05-14 06:23:02

标签: python

在官方文档中10.2. functools  关于部分

大致相当于:

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

我想如果把它重构为:

可能会更好
def partial(func, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        return func(*args, *fargs, **fkeywords, **keywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc

def partial(func, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = keywords.copy()
        newargs = args.copy()
        newkeywords.update(fkeywords)
        args.append(fargs)
        return func(*args, **newkeywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc

我的假设是否有意义?

1 个答案:

答案 0 :(得分:2)

你的第一个版本不太合适,因为the docs解释了:

  

如果提供了其他关键字参数,它们会扩展和覆盖关键字。

当您执行两个关键字splats时,就像在您的版本中一样,您不会覆盖重复的关键字;相反,您会得到TypeError,如Calls中所述:

  

如果语法**表达式出现在函数调用中,则表达式必须求值为映射,其内容被视为附加关键字参数。如果关键字已经存在(作为显式关键字参数,或来自另一个解包),则会引发TypeError异常。

比较

>>> def sub(a, b): return a-b
>>> sub2 = functools.partial(sub, b=2)
>>> sub2(5, b=0)
5
>>> sub2 = partial(sub, b=2)
>>> sub2(5, b=0)
TypeError: f() got multiple values for keyword argument 'b'

您的第二个版本不起作用有四个原因:

  • *args是一个元组,而元组没有copy。不可改变,很少有理由复制它们。
  • 元组也没有append,是不可变的。
  • 即使它们是列表,append也会将新参数列表作为单个参数添加到结尾,但是您希望将它们全部添加为单独的参数。这就是extend的用途。
  • 最后,您使用args代替newargs

您可以通过执行以下操作来解决所有问题:

def newfunc(*fargs, **fkeywords):
    newkeywords = keywords.copy()
    newargs = list(args)
    newkeywords.update(fkeywords)
    newargs.extend(fargs)
    return func(*newargs, **newkeywords)

但是更简单(也更有效)的方式是:

def newfunc(*fargs, **fkeywords):
    newkeywords = keywords.copy()
    newkeywords.update(fkeywords)
    newargs = args + fargs
    return func(*newargs, **newkeywords)

事实上,在Python允许多个splats在3.5中的单个调用之前,这与partial之前的文档非常接近。在3.42.7

def newfunc(*fargs, **fkeywords):
    newkeywords = keywords.copy()
    newkeywords.update(fkeywords)
    return func(*(args + fargs), **newkeywords)

唯一的区别是他们直接在splat中执行args + fargs,而不是将其分配给newargs变量。