【问题标题】:Why does * work differently in assignment statements versus function calls?为什么 * 在赋值语句和函数调用中的工作方式不同?
【发布时间】:2019-06-25 23:11:36
【问题描述】:

在 Python 中,可变参数集合的工作方式似乎与序列解包在赋值语句中的工作方式完全不同。我试图了解这种可能令人困惑的差异的原因。我敢肯定有一个很好的理由,但它是什么?


# Example 1 with assignment statement
a, *b, c = 1,2,3,4
print(b) # [2, 3]

# Example 1 with function call
def foo(a, *b, c):
    print(b)
foo(1,2,3,4)

函数调用导致如下错误:

Traceback (most recent call last):
  File "<pyshell#309>", line 1, in <module>
    foo(1,2,3,4)
TypeError: foo() missing 1 required keyword-only argument: 'c'

问题1:为什么b不赋值类似于赋值语句中的赋值方式?

# Example 2 with function call
def foo(*x):
    print(x)
    print(type(x))
foo(1,2,3) # (1, 2, 3) <class 'tuple'>

# Example 2 with assignment statement
a, *b, c = 1,2,3,4,5
print(b) # [2, 3, 4]
print(type(b)) # <class 'list'>

问题 2:为什么类型不同(列表 vs 元组)?

【问题讨论】:

  • 你应该通读所有相关的PEPs 以找到解释,为什么 Python 功能会如此。
  • 在函数中支持的格式是func(a, b, *args, **kwargs),其中*args是一个元组,**kwargs是一个字典。您不能在可变参数 *b 之后添加位置参数 c。位置参数用于 C 函数,并且有一个带有 func(a,b,/) 格式的 pep 用于在 python 代码上支持它
  • 仅位置参数*

标签: python python-3.x


【解决方案1】:

当在赋值中使用时,Python 将尝试使 *b 匹配它需要的任何内容以使赋值起作用(这是 Python 3 中的新功能,请参阅 PEP 3132)。这些都是有效的:

a, *b, c = 1,4
print(b) # []

a, *b, c = 1,2,3,4,5 
print(b) # [2, 3, 4]

在函数中使用时,如果*b是函数定义中的第二个参数,它将匹配函数调用中倒数第二个参数,如果有的话。当您希望函数接受可变数量的参数时使用它。一些例子:

def foo(a, *b):
    print(b)
foo(1) # ()
foo(1,2,3,4,5) # (2,3,4,5)

我建议您阅读以下内容:


关于列表和元组之间的区别,最大的区别在于可变性。列表是可变的,元组不是。这意味着这有效:

myList = [1, 2, 3]
myList[1] = 4
print(myList) # [1, 4, 3]

这不是:

myTuple = (1, 2, 3)
myTuple[1] = 4 # TypeError: 'tuple' object does not support item assignment

b在这种情况下是一个列表的原因:

a, *b, c = 1,2,3,4,5 
print(b) # [2, 3, 4]

而不是元组(就像在函数中使用 *args 时的情况一样),因为您可能希望在分配后对 b 做一些事情,所以最好将其设为列表,因为列表是可变的。使其成为元组而不是列表是在被接受为功能之前考虑的可能更改之一,如PEP 3132 中所述:

使加星标的目标成为一个元组而不是一个列表。这将是 与函数的 *args 一致,但对 结果更难。

【讨论】:

  • print(b) 应该是 [2, 3, 4] 而不是 [2,3,4,5]
  • 有趣的是,在你引用的中篇文章中,作者也很困惑说:“作为位置传递的参数存储在一个名为 args 的列表中”!这种混淆的可能性就是我的问题。
  • 这是因为你可能想在赋值后对列表做一些事情,在这种情况下,列表比元组更好,因为它是可变的。正如PEP 3132 中所讨论的,将其设置为元组而不是列表是考虑的可能更改之一:“使加星标的目标成为元组而不是列表。这与函数的 *args 一致,但要进一步更难处理结果。”
  • 您能否将这条最新评论合并到答案中?我认为这确实回答了我的问题2。
  • 调用def foo(a, *b, c): 时没有分配c 行为的另一个原因是因为他们希望这种情况允许您实现仅关键字参数。仅关键字参数很有用,因为它们可以防止用户意外传递它们,并允许在 Python 中实现诸如 Python 3 的 print 函数之类的东西,同时允许解释器完成大部分解析参数的工作(而不是接受 @987654341 @ 并且必须自己进行所有关键字解析)。允许您编写自己的def print(*args, sep=' ', end='\n', ...etc...):
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-15
  • 2018-08-25
  • 2017-10-24
  • 2019-10-30
相关资源
最近更新 更多