简体   繁体   English

python中元组解包的语义

[英]Semantics of tuple unpacking in python

Why does python only allow named arguments to follow a tuple unpacking expression in a function call? 为什么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

Is it simply an aesthetic choice, or are there cases where allowing this would lead to some ambiguities? 它只是一种美学选择,还是存在允许这会导致一些含糊不清的情况?

i am pretty sure that the reason people "naturally" don't like this is because it makes the meaning of later arguments ambiguous, depending on the length of the interpolated series: 我很确定人们“自然地”不喜欢这个的原因是因为它使得后面的参数的含义模糊不清,这取决于插值系列的长度:

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

you cannot tell from just looking at the last two calls to dangerbaby which one works as expected and which one kills little kitten fluffykins. 你只能看看最近两次对dangerbaby调用,哪一个按预期工作,哪一个杀死小猫蓬松金属。

of course, some of this uncertainty is also present when interpolating at the end. 当然,在最后插值时也存在一些不确定性。 but the confusion is constrained to the interpolated sequence - it doesn't affect other arguments, like bug . 但混淆受限于插值序列 - 它不会影响其他参数,如bug

[i made a quick search to see if i could find anything official. [我快速搜索,看看我是否能找到任何正式的东西。 it seems that the * prefix for varags was introduced in python 0.9.8 . 似乎varags的*前缀是在python 0.9.8引入的 the previous syntax is discussed here and the rules for how it worked were rather complex. 这里讨论了以前的语法,它的工作原理相当复杂。 since the addition of extra arguments "had to" happen at the end when there was no * marker it seems like that simply carried over. 因为在没有*标记的情况下添加额外的参数“必须”发生在最后,似乎只是简单地延续了。 finally there's a mention here of a long discussion on argument lists that was not by email.] 最后在这里提到了一个关于参数列表的长时间讨论,而不是通过电子邮件。

I suspect that it's for consistency with the star notation in function definitions, which is after all the model for the star notation in function calls. 我怀疑这是为了与函数定义中的星号表示法保持一致,这是函数调用中星号表示法的所有模型。

In the following definition, the parameter *c will slurp all subsequent non-keyword arguments, so obviously when f is called, the only way to pass a value for d will be as a keyword argument. 在下面的定义中,参数*c将会覆盖所有后续的非关键字参数,因此很明显,当调用f时,传递d值的唯一方法是作为关键字参数。

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

(Such "keyword-only parameters" are only supported in Python 3. In Python 2 there is no way to assign values after a starred argument, so the above is illegal.) (这种“唯一关键字参数”在Python 3只支持在Python 2 没有办法星号的参数后,分配值,所以上述是非法的。)

So, in a function definition the starred argument must follow all ordinary positional arguments. 因此,在函数定义中 ,加星标的参数必须遵循所有普通的位置参数。 What you observed is that the same rule has been extended to function calls. 您观察到的是,相同的规则已扩展到函数调用。 This way, the star syntax is consistent for function declarations and function calls. 这样,星形语法对于函数声明和函数调用是一致的。

Another parallelism is that you can only have one (single-)starred argument in a function call. 另一个并行性是在函数调用中只能有一个(单个)星号参数。 The following is illegal, though one could easily imagine it being allowed. 以下是非法的,但很容易想象它被允许。

f(*(1,2), *(3,4))

First of all, it is simple to provide a very similar interface yourself using a wrapper function: 首先,使用包装函数自己提供一个非常相似的界面很简单:

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

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

Secondly, enhancing the interpreter to support your syntax natively might add overhead to the very performance-critical activity of function application. 其次,增强解释器以原生支持您的语法可能会增加功能应用程序的性能关键活动的开销。 Even if it only requires a few extra instructions in compiled code, due to the high usage of those routines, that might constitute an unacceptable performance penalty in exchange for a feature that is not called for all that often and easily accommodated in a user library. 即使它只需要编译代码中的一些额外指令,由于这些例程的高使用率,这可能构成不可接受的性能损失,以换取未被通用且容易地容纳在用户库中的所有特征调用的特征。

Some observations: 一些观察:

  1. Python processes positional arguments before keyword arguments ( f(c=3, *(1, 2)) in your example still prints 1 2 3 ). Python在关键字参数之前处理位置参数( f(c=3, *(1, 2))在您的示例中仍然打印1 2 3 )。 This makes sense as (i) most arguments in function calls are positional and (ii) the semantics of a programming language need to be unambiguous (ie, a choice needs to be made either way on the order in which to process positional and keyword arguments). 这是有道理的,因为(i)函数调用中的大多数参数都是位置的,并且(ii)编程语言的语义需要是明确的(即,需要在处理位置和关键字参数的顺序上进行选择) )。
  2. If we did have a positional argument to the right in a function call, it would be difficult to define what that means. 如果我们在函数调用中确实有一个位置参数,则很难定义这意味着什么。 If we call f(*(1, 2), 3) , should that be f(1, 2, 3) or f(3, 1, 2) and why would either choice make more sense than the other? 如果我们调用f(*(1, 2), 3) ,那应该是f(1, 2, 3)还是f(3, 1, 2) ,为什么这两种选择比另一种更有意义呢?
  3. For an official explanation, PEP 3102 provides a lot of insight on how function definitions work. 对于官方解释, PEP 3102提供了很多关于函数定义如何工作的见解。 The star (*) in a function definition indicates the end of position arguments (section Specification ). 函数定义中的星号(*)表示位置参数的结束(部分规范 )。 To see why, consider: def g(a, b, *c, d) . 要了解原因,请考虑: def g(a, b, *c, d) There's no way to provide a value for d other than as a keyword argument (positional arguments would be 'grabbed' by c ). 除了作为关键字参数之外,没有办法为d提供值(位置参数将由c “抓取”)。
  4. It's important to realize what this means: as the star marks the end of positional arguments, that means all positional arguments must be in that position or to the left of it. 重要的是要意识到这意味着什么:当星标记位置参数的结束时,这意味着所有位置参数必须位于该位置或其左侧。

change the order: 改变顺序:

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

If you have a Python 3 keyword-only parameter, like 如果你有一个Python 3关键字参数,比如

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

then you might expect something like f(*(1, 2), 3) to set a to (1 , 2) and b to 3 , but of course, even if the syntax you want were allowed, it would not, because keyword-only parameters must be keyword-only, like f(*(1, 2), b=3) . 那么你可能期望像f(*(1, 2), 3)这样设置a(1 , 2)b3 ,但是当然,即使你想要的语法被允许,它也不会,因为关键字-only参数必须是仅关键字,如f(*(1, 2), b=3) If it were allowed, I suppose it would have to set a to (1, 2, 3) and leave b as the default 1 . 如果允许,我想它会设置a(1, 2, 3)并留下b作为默认1 So it's perhaps not syntactic ambiguity so much as ambiguity in what is expected, which is something Python greatly tries to avoid. 因此,它可能不是语法上的模糊性,而是预期的模糊性,这是Python极大地试图避免的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM