python中元组解包的语义

时间:2012-05-23 17:56:09

标签: python tuples iterable-unpacking

为什么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

它只是一种美学选择,还是存在允许这会导致一些含糊不清的情况?

6 个答案:

答案 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)

一些观察结果:

  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. 重要的是要意识到这意味着什么:因为星星标志着位置参数的结束,这意味着所有位置参数必须位于该位置或左侧。

答案 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)b3,但当然,即使您需要语法被允许,但不会,因为仅限关键字的参数必须是关键字,例如f(*(1, 2), b=3)。如果允许,我认为必须将a设置为(1, 2, 3)并将b保留为默认1。因此,它可能不是语法上的模糊性,而是预期的模糊性,这是Python极大地试图避免的。