Можно ли указывать * аргументы после предоставления аргументов ключевого слова? - PullRequest
0 голосов
/ 12 марта 2020

Я пытаюсь переопределить функцию, сначала предоставив все позиционные и непозиционные аргументы, а затем предоставив остальную часть аргумента без ключевого слова.

Вот эта функция с предпринятые шаги:

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 et c.)?

РЕДАКТИРОВАТЬ:

Вот реальный код проблемы с 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()),
        )

Traceback:

>>> 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 голосов
/ 12 марта 2020

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}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...