为什么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
它只是一种美学选择,还是存在允许这会导致一些含糊不清的情况?
答案 0 :(得分:29)
我很确定人们“自然地”不喜欢这个的原因是因为它使得后面的参数的含义模糊不清,这取决于插值序列的长度:
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
。
[我快速搜索,看看我是否能找到任何正式的东西。似乎varags的*前缀是introduced in python 0.9.8。以前的语法是discussed here,其工作原理的规则相当复杂。因为在没有*标记的情况下添加额外的参数“必须”发生在最后,似乎只是简单地延续了。最后,对于参数列表进行了长时间的mention here讨论,而不是通过电子邮件。]
答案 1 :(得分:6)
我怀疑这是为了与函数定义中的星号表示法保持一致,这是函数调用中星号表示法的所有模型。
在以下定义中,参数*c
将覆盖所有后续非关键字参数,因此显然在调用f
时,传递d
值的唯一方法是作为关键字参数。
def f(a, b, *c, d=1):
print "slurped", len(c)
(这样的“仅关键字参数”仅在Python 3中受支持。在Python 2中, 无法在加星标的参数后分配值,因此上述内容是非法的。)
因此,在函数 definition 中,加星标的参数必须遵循所有普通的位置参数。您观察到的是,相同的规则已扩展到函数调用。这样,星形语法对于函数声明和函数调用是一致的。
另一个并行性是在函数调用中只能有一个(单个)星号参数。以下是非法的,但很容易想象它被允许。
f(*(1,2), *(3,4))
答案 2 :(得分:1)
首先,使用包装函数自己提供一个非常相似的界面很简单:
def applylast(func, arglist, *literalargs):
return func(*(literalargs + arglist))
applylast(f, (1, 2), 3) # equivalent to f(3, 1, 2)
其次,增强解释器以原生支持您的语法可能会增加功能应用程序的性能关键活动的开销。即使它只需要编译代码中的一些额外指令,由于这些例程的高使用率,这可能构成不可接受的性能损失,以换取未被通用且容易地容纳在用户库中的所有特征调用的特征。
答案 3 :(得分:1)
一些观察结果:
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
“抓取”)。答案 4 :(得分:0)
更改顺序:
def f(c,a,b):
print(a,b,c)
f(3,*(1,2))
答案 5 :(得分:0)
如果你有一个Python 3关键字参数,比如
def f(*a, b=1):
...
那么您可能希望将f(*(1, 2), 3)
设置为a
至(1 , 2)
和b
至3
,但当然,即使您需要语法被允许,但不会,因为仅限关键字的参数必须是关键字,例如f(*(1, 2), b=3)
。如果允许,我认为必须将a
设置为(1, 2, 3)
并将b
保留为默认1
。因此,它可能不是语法上的模糊性,而是预期的模糊性,这是Python极大地试图避免的。