【问题标题】:Corner case with positional-only parameters in Python 3.8?Python 3.8 中仅位置参数的极端情况?
【发布时间】:2019-10-30 08:58:13
【问题描述】:

我正在摆弄PEP 570 中指定并由Python 3.8 引入的仅位置参数,我只是想知道一个特定的极端情况。

假设我定义一个函数如下(无论这是好的设计还是有意义):

def func(p1, p2=None, p3=None, /): 
    print(p1, p2, p3)

所以有一个必需参数(p1),后跟两个可选参数(p2p3)。我可以只用p1p1p2p1p2p3 调用该函数:

func(1)       # 1, None, None
func(1, 2)    # 1, 2, None
func(1, 2, 3) # 1, 2, 3

但是我无法只使用p1p3 的参数调用它,同时保持p2 的默认值,因为我无法提供关键字参数:

func(1, p3=3)

这当然会引发TypeError

TypeError: func() got some positional-only arguments passed as keyword arguments: 'p3'

我找不到关于这种情况的任何讨论或示例,因为PEP 570 中的所有示例都只包含一个可选参数作为仅位置参数的一部分:

def name(p1, p2, /, p_or_kw, *, kw):
def name(p1, p2=None, /, p_or_kw=None, *, kw):
def name(p1, p2=None, /, *, kw):
def name(p1, p2=None, /):
def name(p1, p2, /, p_or_kw):
def name(p1, p2, /):

所以我的问题是:这是预期的行为,让调用者从左到右提供多个可选参数,以强制顺序覆盖它们吗?这实际上是仅位置参数的一个特性吗?

【问题讨论】:

  • "Is that the intended behavior, to have a caller provide multiple optional arguments from left to right, overriding them in a forced order? Is this actually a feature of positional-only arguments?"我可能没有正确理解你,但这不仅是位置参数的“预期行为”,它几乎就是它的定义。
  • func(1, p3=3) 直接与函数定义中 / 的使用相矛盾,因为它为只接受位置参数的函数提供了关键字参数。 p2 具有默认值的事实无关紧要
  • > func(1, p3=3) 直接与 / 的使用相矛盾,这只是为了说明仅覆盖单个默认参数是不可能的。我只是对仅位置参数中的多个默认值感到困惑,但这似乎是您真正想要的行为。我只是发现没有明确提及该行为。
  • 对于 positional-or-keyword 参数,有一个概念是“这里有一些有意义的默认值,如果你愿意,你可以单独覆盖它们”,而对于 positional-only 也有默认值,但它更像是“这里有一些默认值,您可以一一覆盖它们,但也只能按照我们告诉您的顺序”。我想这让我感到困惑,在仅位置上下文中,默认参数具有不同的语义。但这很有意义,我想问题已经得到解答。

标签: python arguments python-3.8 default-arguments pep570


【解决方案1】:

这是预期的行为,让调用者从左到右提供多个可选参数,以强制顺序覆盖它们吗?这实际上是仅位置参数的一个特性吗?

这不仅是位置参数的“预期行为”,而且几乎是它的定义。

func(1, p3=3) 直接与函数签名中/ 的使用相矛盾,因为它为只接受位置参数的函数提供了关键字参数。 p2 具有默认值这一事实无关紧要(尽管您发现它几乎没用)。

我将继续在文档中寻找明确的解释,但可能没有。这基本上是使用/ 的直接含义。

然而PEP570 includes this example

def name(positional_only_parameters, /, positional_or_keyword_parameters,
         *, keyword_only_parameters):

这表明我们可以将func 重写为:

def func(p1,p3=None, /, p2=None):
    print(p1, p2, p3)

那么这两个工作:

func(1, 3)
func(1, 3, 2)

输出是

1 None 3
1 2 3

Try it online

【讨论】:

  • 当然,它还可以让人们像func(1, 3, p2=2) 甚至func(1, p2=2) 一样称呼它。但那是一种不同的签名,因为它将p2 移动到位置或关键字参数中。但无论如何你是对的,这是预期的行为。我只是对多个可选参数在仅位置上下文中的新语义感到困惑,但它实际上是你想要的。
  • 在位置或关键字上下文中,调用者可以以任意组合和顺序(如樱桃采摘)单独传递多个可选参数,而在仅位置上下文中,可以覆盖默认参数,但只能从左到右,按照签名强加的顺序一一对应。这是我以前没有抓住的重点。
【解决方案2】:

eval Builtin 是一个很好的例子,它有两个仅用于位置的可选参数:

eval(source, globals=None, locals=None, /)
    Evaluate the given source in the context of globals and locals.

    The source may be a string representing a Python expression
    or a code object as returned by compile().
    The globals must be a dictionary and locals can be any mapping,
    defaulting to the current globals and locals.
    If only globals is given, locals defaults to it.

所以没有办法只指定locals:

>>> eval("1 + 2 + x", locals={'x': 3})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: eval() takes no keyword arguments

在位置上,必须首先提供globals(如果省略了locals,则它们默认为globals):

>>> eval("1 + 2 + x", {'x': 3}) # globals = locals
6

...或者,如果它们应该不同:

>>> eval("1 + 2 + x", {'x': 3}, {'x': 4})
7

所以,回答这个问题:这正是预期的行为。

【讨论】:

    猜你喜欢
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多