python中元组解包的语义

python中元组解包的语义,python,tuples,iterable-unpacking,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 定义f(a、b、c): ... 打印a、b、c ... >>>f(*(1,2),3) 文件

为什么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
定义f(a、b、c): ... 打印a、b、c ... >>>f(*(1,2),3) 文件“”,第1行 SyntaxError:只有命名参数可以跟在*表达式后面 这仅仅是一种审美选择,还是在某些情况下允许这样做会导致一些歧义?

改变顺序:

def f(c,a,b):
    print(a,b,c)
f(3,*(1,2))

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

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

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

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

我很确定人们“自然”地不喜欢这一点是因为它使后面参数的含义变得模糊,这取决于插值序列的长度:

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
只要看最后两次调用
dangeraby
,你就无法分辨出哪一个按预期工作,哪一个杀死了小猫绒毛

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


[我快速搜索了一下,看看是否能找到任何官方的信息。似乎varags的*前缀是。前面的语法是,它的工作规则相当复杂。因为添加了额外的参数“不得不”最后发生在没有*标记的情况下,这似乎只是简单地延续了下来。最后,关于参数列表有一个很长的讨论,不是通过电子邮件进行的。]

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

在以下定义中,参数
*c
将发出所有后续非关键字参数的声音,因此显然,当调用
f
时,传递
d
值的唯一方法将作为关键字参数

def f(a, b, *c, d=1):
    print "slurped", len(c)
(只有Python3才支持这种“仅关键字参数”。在Python2中,无法在带星号的参数后赋值,因此上述操作是非法的。)

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

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

f(*(1,2), *(3,4))
一些意见:

  • Python在关键字参数之前处理位置参数(
    f(c=3,*(1,2))
    在您的示例中仍然打印
    13
    )。这是有意义的,因为(i)函数调用中的大多数参数是位置参数,(ii)编程语言的语义需要明确(即,需要选择处理位置参数和关键字参数的顺序)
  • 如果我们在函数调用的右边有一个位置参数,那么很难定义它的含义。如果我们称之为
    f(*(1,2,3)
    ,那应该是
    f(1,2,3)
    还是
    f(3,1,2)
    ,为什么两种选择中的任何一种都比另一种更有意义
  • 作为官方解释,它提供了许多关于函数定义如何工作的见解。函数定义中的星号(*)表示位置参数的结束(部分规范)。要了解原因,请考虑:
    def g(a,b,*c,d)
    。除了作为关键字参数之外,无法为
    d
    提供值(位置参数将被
    c
    捕获)
  • 认识到这意味着什么很重要:因为星形标志着位置参数的结束,这意味着所有位置参数都必须位于该位置或其左侧

  • 如果您有一个Python3纯关键字参数,比如

    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极力避免的

    您可以在解包之前将最后一个参数添加到元组中。这是一个有趣的问题。我似乎还记得python 2.5前后更改的规则-您过去必须将
    *args
    **kwargs
    放在末尾,但我找不到changelog条目。规则是参数应遵循以下顺序:
    (args,name=args,*args,**args)
    是的,但为什么这是一条规则呢?因为如果你可以按任何顺序做事,那么当人们以任何方式编写东西时,很难遵循代码,那么你最好不要使用Python。换句话说,因为Guido这么说,这就是为什么:)@Karmel,我很好奇为什么函数参数必须是这个顺序。我已经阅读了一些PEP邮件列表讨论,Guido做出的大多数决定都是出于非常好、具体和实际的原因,而不仅仅是“因为他这么说”。@user545424实际上有些是“因为他这么说”,就像字典迭代一样。迭代字典迭代键,有些人认为应该是键,对值,但他说我