为什么 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
这仅仅是一种审美选择,还是在某些情况下允许这样做会导致一些歧义?
为什么 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
这仅仅是一种审美选择,还是在某些情况下允许这样做会导致一些歧义?
我很确定人们“自然”不喜欢这个的原因是因为它使后面的论点的含义模棱两可,具体取决于插值序列的长度:
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 的 * 前缀。前面的语法在这里讨论,它的工作规则相当复杂。由于在没有 * 标记的情况下“必须”添加额外的参数,因此似乎只是延续了。最后这里提到了关于参数列表的长时间讨论,而不是通过电子邮件。]
我怀疑这是为了与函数定义中的星号保持一致,毕竟这是函数调用中星号的模型。
在下面的定义中,参数*c
将 slurp 所有后续的非关键字参数,所以很明显,当f
被调用时,传递值的唯一方法d
是作为关键字参数。
def f(a, b, *c, d=1):
print "slurped", len(c)
(这种“仅关键字参数”仅在 Python 3 中受支持。在 Python 2 中,无法在带星号的参数之后分配值,因此上述内容是非法的。)
因此,在函数定义中,带星号的参数必须跟在所有普通的位置参数之后。您观察到的是,相同的规则已扩展到函数调用。这样,星型语法对于函数声明和函数调用是一致的。
另一个并行性是在函数调用中只能有一个(单)星号参数。以下是非法的,尽管人们很容易想象它是被允许的。
f(*(1,2), *(3,4))
首先,使用包装函数自己提供一个非常相似的接口很简单:
def applylast(func, arglist, *literalargs):
return func(*(literalargs + arglist))
applylast(f, (1, 2), 3) # equivalent to f(3, 1, 2)
其次,增强解释器以原生支持您的语法可能会增加函数应用程序对性能非常关键的活动的开销。即使它只需要编译代码中的一些额外指令,由于这些例程的高使用率,这可能构成不可接受的性能损失,以换取用户库中不经常调用且容易容纳的特性。
一些观察:
f(c=3, *(1, 2))
在您的示例中仍然打印1 2 3
)。这是有道理的,因为 (i) 函数调用中的大多数参数都是位置参数,并且 (ii) 编程语言的语义需要明确(即,需要根据处理位置参数和关键字参数的顺序进行选择) )。f(*(1, 2), 3)
,那应该是,f(1, 2, 3)
还是f(3, 1, 2)
为什么任何一个选择比另一个更有意义?def g(a, b, *c, d)
:d
除了作为关键字参数之外,没有其他方法可以提供值(位置参数将被 '抓住' c
)。更改顺序:
def f(c,a,b):
print(a,b,c)
f(3,*(1,2))
如果你有一个 Python 3 关键字参数,比如
def f(*a, b=1):
...
那么您可能希望f(*(1, 2), 3)
设置a
为(1 , 2)
和b
to之类的东西3
,但是当然,即使您想要的语法被允许,它也不会,因为仅关键字参数必须是仅关键字参数,例如f(*(1, 2), b=3)
. 如果允许,我想它必须设置a
为(1, 2, 3)
并保留b
为默认值1
。因此,可能与其说是语法上的歧义,不如说是在预期中的歧义,这是 Python 极力避免的。