使用`*args`,关键字参数变成位置?
with `*args`, keyword arguments become positional?
例如,假设我定义了一个函数
def myfunc(a=1, *args):
print(a)
# *args have some other functionality, for example passed to a class instance, or whatever
问题是:当调用 myfunc
时,我无法先传递 *args
的预期位置参数,然后再传递关键字参数 a
。例如,如果预期的 *args
只包含一个数字,我不能做
myfunc(3, a=4)
这会引发一个错误,说明收到了 a 的多个作业。 myfunc(a=4, b=5, 3)
显然我做不到。我只能做
myfunc(4, 3)
所以现在看来,有了 *args
,关键字参数 a
真的变成了位置?
这里的例子是函数,但也适用于classes。假设一个带有 __init__(a=3, *args)
的 subclass 想要一些关键字参数 a
和一些其他 99 个必需的参数传递给 super。我不想重复 subclass 中的 99 个参数定义,而是 *args
并将其传递给 super。但是现在关键字参数 a
变成了位置参数。
这个 class 和 subclass 例子是我编码中的真正问题。我想要一个带有可选关键字参数 a
的 subclass,然后是其 super 的 99 个必需参数。我没有找到解决这个问题的好方法。
我不是第一个遇到这个问题的人,但我找不到任何答案。
a
不是仅限位置的:
>>> import inspect
>>> def myfunc(a=1, *args):
... print(a)
>>> inspect.signature(myfunc).parameters['a'].kind
<_ParameterKind.POSITIONAL_OR_KEYWORD: 1>
而且确实可以作为关键字参数传入:
>>> myfunc(a=4)
4
但是,正如您所指出的,一旦您想提供后续位置参数,就必须将其作为位置参数提供。但这与 *args
无关,这就是位置参数的工作方式:
>>> def myfunc2(a, b):
... print(a)
>>> myfunc2(1, a=2)
TypeError: myfunc2() got multiple values for argument 'a'
它要么必须是所有位置参数的第一个 要么 所有参数都必须按名称传递。
然而 *args
使情况变得有些复杂,因为它只收集位置参数。这意味着如果它应该收集 anything 你还必须按位置传递所有先前的参数。
但是如果你想推出自己的特殊情况,你可以只接受 *args, **kwargs
并首先检查它是否作为关键字(命名)参数传递,或者如果没有,作为第一个位置参数传递:
>>> def myfunc3(*args, **kwargs):
... if 'a' in kwargs:
... a = kwargs.pop('a')
... else:
... a = args[0]
... args = args[1:]
... print(a, args, kwargs)
>>> myfunc3(1, a=2)
2 (1,) {}
>>> myfunc3(2, 1)
2 (1,) {}
这应该会给你预期的结果,但你需要在文档中明确说明参数应该如何传递,因为它在某种程度上破坏了 Python 签名模型的语义。
Python 3 引入了仅关键字参数:https://www.python.org/dev/peps/pep-3102/
所以你可以这样做:
def myfunc(*args, a=1):
print(a)
然后 myfunc(3, a=4)
就可以了。
例如,假设我定义了一个函数
def myfunc(a=1, *args):
print(a)
# *args have some other functionality, for example passed to a class instance, or whatever
问题是:当调用 myfunc
时,我无法先传递 *args
的预期位置参数,然后再传递关键字参数 a
。例如,如果预期的 *args
只包含一个数字,我不能做
myfunc(3, a=4)
这会引发一个错误,说明收到了 a 的多个作业。 myfunc(a=4, b=5, 3)
显然我做不到。我只能做
myfunc(4, 3)
所以现在看来,有了 *args
,关键字参数 a
真的变成了位置?
这里的例子是函数,但也适用于classes。假设一个带有 __init__(a=3, *args)
的 subclass 想要一些关键字参数 a
和一些其他 99 个必需的参数传递给 super。我不想重复 subclass 中的 99 个参数定义,而是 *args
并将其传递给 super。但是现在关键字参数 a
变成了位置参数。
这个 class 和 subclass 例子是我编码中的真正问题。我想要一个带有可选关键字参数 a
的 subclass,然后是其 super 的 99 个必需参数。我没有找到解决这个问题的好方法。
我不是第一个遇到这个问题的人,但我找不到任何答案。
a
不是仅限位置的:
>>> import inspect
>>> def myfunc(a=1, *args):
... print(a)
>>> inspect.signature(myfunc).parameters['a'].kind
<_ParameterKind.POSITIONAL_OR_KEYWORD: 1>
而且确实可以作为关键字参数传入:
>>> myfunc(a=4)
4
但是,正如您所指出的,一旦您想提供后续位置参数,就必须将其作为位置参数提供。但这与 *args
无关,这就是位置参数的工作方式:
>>> def myfunc2(a, b):
... print(a)
>>> myfunc2(1, a=2)
TypeError: myfunc2() got multiple values for argument 'a'
它要么必须是所有位置参数的第一个 要么 所有参数都必须按名称传递。
然而 *args
使情况变得有些复杂,因为它只收集位置参数。这意味着如果它应该收集 anything 你还必须按位置传递所有先前的参数。
但是如果你想推出自己的特殊情况,你可以只接受 *args, **kwargs
并首先检查它是否作为关键字(命名)参数传递,或者如果没有,作为第一个位置参数传递:
>>> def myfunc3(*args, **kwargs):
... if 'a' in kwargs:
... a = kwargs.pop('a')
... else:
... a = args[0]
... args = args[1:]
... print(a, args, kwargs)
>>> myfunc3(1, a=2)
2 (1,) {}
>>> myfunc3(2, 1)
2 (1,) {}
这应该会给你预期的结果,但你需要在文档中明确说明参数应该如何传递,因为它在某种程度上破坏了 Python 签名模型的语义。
Python 3 引入了仅关键字参数:https://www.python.org/dev/peps/pep-3102/
所以你可以这样做:
def myfunc(*args, a=1):
print(a)
然后 myfunc(3, a=4)
就可以了。