28

为什么 python 在函数调用中只允许命名参数跟随元组解包表达式?

>>> def f(a,b,c):
...     print a, b, c
... 
>>> f(*(1,2),3)
  File "<stdin>", line 1
SyntaxError: only named arguments may follow *expression

这仅仅是一种审美选择,还是在某些情况下允许这样做会导致一些歧义?

4

6 回答 6

31

我很确定人们“自然”不喜欢这个的原因是因为它使后面的论点的含义模棱两可,具体取决于插值序列的长度:

def dangerbaby(a, b, *c):
    hug(a)
    kill(b) 

>>> dangerbaby('puppy', 'bug')
killed bug
>>> cuddles = ['puppy']
>>> dangerbaby(*cuddles, 'bug')
killed bug
>>> cuddles.append('kitten')
>>> dangerbaby(*cuddles, 'bug')
killed kitten

仅查看最后两个调用,您无法分辨出dangerbaby哪一个按预期工作,哪一个杀死了小猫毛茸茸的小猫。

当然,在最后进行插值时,也存在一些不确定性。但混淆仅限于插值序列 - 它不会影响其他参数,例如bug.

[我快速搜索了一下是否可以找到任何官方信息。似乎在 python 0.9.8 中引入了 varags 的 * 前缀。前面的语法在这里讨论,它的工作规则相当复杂。由于在没有 * 标记的情况下“必须”添加额外的参数,因此似乎只是延续了。最后这里提到了关于参数列表的长时间讨论,而不是通过电子邮件。]

于 2012-05-23T22:10:55.393 回答
6

我怀疑这是为了与函数定义中的星号保持一致,毕竟这是函数调用中星号的模型。

在下面的定义中,参数*c将 slurp 所有后续的非关键字参数,所以很明显,当f被调用时,传递值的唯一方法d是作为关键字参数。

def f(a, b, *c, d=1):
    print "slurped", len(c)

(这种“仅关键字参数”仅在 Python 3 中受支持。在 Python 2 中,无法在带星号的参数之后分配值,因此上述内容是非法的。)

因此,在函数定义中,带星号的参数必须跟在所有普通的位置参数之后。您观察到的是,相同的规则已扩展到函数调用。这样,星型语法对于函数声明和函数调用是一致的。

另一个并行性是在函数调用中只能有一个(单)星号参数。以下是非法的,尽管人们很容易想象它是被允许的。

f(*(1,2), *(3,4))
于 2012-06-08T23:22:47.907 回答
1

首先,使用包装函数自己提供一个非常相似的接口很简单:

def applylast(func, arglist, *literalargs):
  return func(*(literalargs + arglist))

applylast(f, (1, 2), 3)  # equivalent to f(3, 1, 2)

其次,增强解释器以原生支持您的语法可能会增加函数应用程序对性能非常关键的活动的开销。即使它只需要编译代码中的一些额外指令,由于这些例程的高使用率,这可能构成不可接受的性能损失,以换取用户库中不经常调用且容易容纳的特性。

于 2012-05-23T19:20:16.287 回答
1

一些观察:

  1. Python 在关键字参数之前处理位置参数(f(c=3, *(1, 2))在您的示例中仍然打印1 2 3)。这是有道理的,因为 (i) 函数调用中的大多数参数都是位置参数,并且 (ii) 编程语言的语义需要明确(即,需要根据处理位置参数和关键字参数的顺序进行选择) )。
  2. 如果我们在函数调用中确实有一个位于右侧的位置参数,那么很难定义它的含义。如果我们打电话f(*(1, 2), 3),那应该是,f(1, 2, 3)还是f(3, 1, 2)为什么任何一个选择比另一个更有意义?
  3. 对于官方解释,PEP 3102提供了很多关于函数定义如何工作的见解。函数定义中的星号 (*)表示位置参数的结束(规范部分)。要了解原因,请考虑def g(a, b, *c, d)d除了作为关键字参数之外,没有其他方法可以提供值(位置参数将被 '抓住' c)。
  4. 重要的是要意识到这意味着什么:因为星号标志着位置参数的结束,这意味着所有位置参数都必须在那个位置或者在它的左边。
于 2012-06-12T19:16:01.697 回答
0

更改顺序:

def f(c,a,b):
    print(a,b,c)
f(3,*(1,2))
于 2012-05-23T18:01:22.730 回答
0

如果你有一个 Python 3 关键字参数,比如

def f(*a, b=1):
    ...

那么您可能希望f(*(1, 2), 3)设置a(1 , 2)bto之类的东西3,但是当然,即使您想要的语法被允许,它也不会,因为仅关键字参数必须是仅关键字参数,例如f(*(1, 2), b=3). 如果允许,我想它必须设置a(1, 2, 3)并保留b为默认值1。因此,可能与其说是语法上的歧义,不如说是在预期中的歧义,这是 Python 极力避免的。

于 2012-06-13T20:51:58.833 回答