提供关键字参数后可以给出* args吗?

时间:2020-03-12 11:19:56

标签: python python-3.x tkinter

我试图通过先提供所有位置和非位置参数,然后再提供其余非关键字参数来覆盖函数。

具有以下步骤的功能:

def foo(a, b, c=3, *args, **kwargs):
    print(f"Positional: a={a} | b={b} | c={c}")
    print("*args", args)
    print("**kwargs", kwargs)

from functools import partial

# a partial callable to supply *args later 
part = partial(foo, a=2, b=4, c=3, extra=3)

# Here, I expect that the extra arguments would be called
# but instead raises TypeError
part('a', 'b', 'c')

但是出现了错误。

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-7-bf0b47424a01> in <module>()
     11 # Here, I expect that the extra arguments would be called
     12 # but instread raises TypeError
---> 13 part('a', 'b', 'c')

TypeError: foo() got multiple values for argument 'a'

是否有解决此问题的方法,而无需深入研究它 可调用对象的签名(例如使用inspect.signature等)?

编辑:

这是Traceback的真正问题代码:

import tkinter as tk
from tkinter import ttk


def _builder(parent, label, wtype, *args, **kwargs):
    """
    Function to build widgets for configbox.
    """
    # creae a container frame with the parent
    container = ttk.Frame(parent)
    container.pack(fill='x')

    # add the elements in the container
    # =================================
    # pack label
    label = ttk.Label(container, text=label)
    label.pack(side='left', padx=4, pady=4, fill='x')

    # get the widget by name and instantiate
    # (fails here)
    widget = getattr(ttk, wtype)(container, *args, **kwargs)

    # pack and return
    widget.pack(side='right', padx=4, pady=4, fill='x')
    return widget


class _ConfigBox(tk.Toplevel):
    def __init__(self, parent, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)

        lframe = ttk.Labelframe(self, text="Configurations")
        lframe.pack()

        # this does not fail - (no *args or **kwargs)
        # place extension entry box
        ext_entry = _builder(lframe, "Extension", "Entry")

        # THIS PART FAILS!
        # bind backend variable with option menu
        bkndvar = tk.StringVar(lframe, value="# XXX TEST")
        bknd_opts = _builder(
            lframe,
            "Backend",
            "OptionMenu",

            # give the *args...
            *("TEST1", "TEST2"),

            # ...and the **kwargs (fails here)
            **dict(variable=bkndvar, default=bkndvar.get()),
        )

回溯:

>>> x = _ConfigBox(tk.Tk())
>>> x.mainloop()

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-7-d0fface65de6> in <module>()
----> 1 x = _ConfigBox(tk.Tk())
      2 x.mainloop()

<ipython-input-6-aad9dde31798> in __init__(self, parent, *args, **kwargs)
     45 
     46             # ...and the **kwargs (fails here)
---> 47             **dict(variable=bkndvar, default=bkndvar.get()),
     48         )

<ipython-input-6-aad9dde31798> in _builder(parent, label, wtype, *args, **kwargs)
     15     # get the widget by name and instantiate
     16     # (fails here)
---> 17     widget = getattr(ttk, wtype)(container, *args, **kwargs)
     18 
     19     # pack and return

TypeError: __init__() got multiple values for argument 'variable'

1 个答案:

答案 0 :(得分:0)

partial的关键字参数与其包装的函数的命名参数不匹配;它只是保存它们以将其添加到partial对象以后接收的参数中。也就是说,part(foo, a=2, b=4, c=3, extra=3)('a', 'b', 'c')等同于foo('a', 'b', 'c', a=2, b=4, c=3, extra=3)

您需要将位置参数传递给对partial的调用。

part = partial(foo, 2, 4, 3, extra=3)

然后,您将获得所需的结果:

>>> part('a', 'b', 'c')
Positional: a=2 | b=4 | c=3
*args ('a', 'b', 'c')
**kwargs {'extra': 3}