这些 `.` 属性绑定在 `functools.partial` 的实现中是必需的吗?

Are these `.` attribute bindings necessary in the implementation of `functools.partial`?

docs.python.org 表示 functools.partial 大致相当于:

def partial(func, /, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = {**keywords, **fkeywords}
        return func(*args, *fargs, **newkeywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc

(注意:/ 用于表示 func 作为 partial 的仅位置参数。参见 [1]。)

如果我没理解错的话,当在嵌套函数中引用变量时,例如newfunc,Python首先在嵌套函数中查找变量定义。如果在那里找不到定义,Python 接下来将在封闭范围内查找定义(即外部函数;在本例中为 partial)。那么,上面 newfunc 的显式 .func.args.keywords 属性绑定真的有必要吗?我尝试了一个没有上述绑定的示例,它 partial 工作得很好吗?是否存在可能需要它们的情况?

def partial(func, /, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = {**keywords, **fkeywords}
        return func(*args, *fargs, **newkeywords)
    # newfunc.func = func
    # newfunc.args = args
    # newfunc.keywords = keywords
    return newfunc

# p0, p1, p2 are positional arguments
# kw0, kw1, kw2 are keyword-only arguments
def foo3(p0, p1, p2, *, kw0, kw1, kw2):
    return 100*p2 + 10*p1 + 1*p0, kw0 + kw1 + kw3

foo2 = partial(foo3, 1, kw0=1+1j)

print(foo2(2,3,kw1=2+2j, kw2=3+3j)) # (321, (6+6j))

如果 keywordsfkeywords 词典包含带有 funcargskeywords 的项目,是否需要 . 绑定作为关键字?这些是必要的例子是什么?据我所知,这不是原因,因为以下工作:

def partial(func, /, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = {**keywords, **fkeywords}
        return func(*args, *fargs, **newkeywords)
    # newfunc.func = func
    # newfunc.args = args
    # newfunc.keywords = keywords
    return newfunc

# p0, p1, p2 are positional arguments
# kw0, kw1, kw2 are keyword-only arguments
def foo3(p0, p1, p2, kw0, kw1, kw2, **kwargs):
    return 100*p2 + 10*p1 + 1*p0, kw0 + kw1 + kw2 + sum(kwargs.values())

foo2 = partial(foo3, 1, kw0=1+1J, func=10, args=10j, keywords=100+100j)

print(foo2(2,3,kw1=2+2J, kw2=3+3J, func=20, args=20j, keywords=200+200j)) # (321, (226+6j))

[1] https://docs.python.org/3/whatsnew/3.8.html#positional-only-parameters

我想你可以看看 partial class 实现,以帮助你更好地理解。

以下(Python 3.9.5)

class partial:
    """New function with partial application of the given arguments
    and keywords.
    """

    __slots__ = "func", "args", "keywords", "__dict__", "__weakref__"

    def __new__(cls, func, /, *args, **keywords):
        if not callable(func):
            raise TypeError("the first argument must be callable")

        if hasattr(func, "func"):
            args = func.args + args
            keywords = {**func.keywords, **keywords}
            func = func.func

        self = super(partial, cls).__new__(cls)

        self.func = func
        self.args = args
        self.keywords = keywords
        return self

    def __call__(self, /, *args, **keywords):
        keywords = {**self.keywords, **keywords}
        return self.func(*self.args, *args, **keywords)
    
    ...

当您将 self 替换为 newfunc 时,它们几乎是一样的。