首页 > 解决方案 > Python `inspect.Signature` 将所有定义的位置参数显示为 `ParameterKind.POSITIONAL_OR_KEYWORD`

问题描述

我的印象是您可以使用该inspect.Signature函数来检索和区分位置参数和关键字参数。然而,情况似乎并非如此:

def foo(a,b,c, t=3, q=5):
    print(a,b,c,t,q)

[(u, u.kind) for u in i.signature(foo).parameters.values()]

[(<Parameter "a">, <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>),
 (<Parameter "b">, <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>),
 (<Parameter "c">, <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>),
 (<Parameter "t=3">, <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>),
 (<Parameter "q=5">, <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>)]

因此,实际上,该kind属性似乎无法区分位置参数和关键字参数

所以这里的问题是:

如何区分参数a,b,c和参数t,q,以便在调用 foo 时:

  #build args from signature with args = [a,b,c]
  #build kwargs from signature with kwargs = {'t': t,'q': q}
  foo(*args, **kwargs)

标签: python-3.xreflectioninspect

解决方案


TL;博士

除非您将函数参数显式定义为isPOSITIONAL_ONLYKEYWORD_ONLY使用以下语法,否则默认行为是所有参数为 be POSITIONAL_OR_KEYWORD。Python 3.8+ 的行为与以前的 Python 版本之间存在差异。

Python 3.8+

在 Python 3.8+ 中,参数是仅位置还是仅关键字可以分别使用/and*语法指定。举个例子:

def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
      -----------    ----------     ----------
      |              |              |
      |              Positional or  |
      |              keyword        Keyword only
      Positional only

之前的一切都/只是位置;之后的所有内容*都是关键字。请注意,顺序很重要——/必须在 之前*。此外,如果您没有仅通过语法明确指定位置或关键字,则所有参数都是位置或关键字kind

这是由于在 Python 3.8 中所做的更改。/语法的行为在PEP 570中指定(在PEP 457之后)。在代码中:

>>> import inspect

# Positional or keyword (default behavior)
>>> def meow (a, b, c = 0, d = 1):
...    return (a * b + c) * d

>>> {p.name: p.kind for p in inspect.signature(meow).parameters.values()}
{'a': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>, 
 'b': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>, 
 'c': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>, 
 'd': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>}

# Positional only, positional or keyword, keyword only
>>> def meow (a, /, b, c = 0, *, d = 1):
...    return (a * b + c) * d

>>> {p.name: p.kind for p in inspect.signature(meow).parameters.values()}
{'a': <_ParameterKind.POSITIONAL_ONLY: 1>, 
 'b': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>, 
 'c': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>, 
 'd': <_ParameterKind.KEYWORD_ONLY: 1>}

在 Python 3.8 之前

在 PEP 570 之前,/语法不存在,但*语法存在(引入时无法找到确切的 PEP);在 3.7 中尝试/会引发语法错误:

#Python 3.7
>>> def meow (a, /, b, c = 0, *, d = 1):
  File "<stdin>", line 1
    def meow (a, /, b, c = 0, *, d = 1):

# If we just omit the `/` and keep the `*`, it works
>>> def meow (a, b, c = 0, *, d = 1):
...    return (a * b + c) * d

>>> {p.name: p.kind for p in inspect.signature(meow).parameters.values()}
{'a': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>, 
 'b': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>, 
 'c': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>, 
 'd': <_ParameterKind.KEYWORD_ONLY: 1>}

除了 PEP,我还发现这个快速总结有助于理解行为。


推荐阅读