【问题标题】:What does the comma in this assignment statement do?这个赋值语句中的逗号有什么作用?
【发布时间】:2023-04-03 00:06:01
【问题描述】:

我正在查看我发现的一个有趣的示例脚本(在this site,最后一个示例第 124 行),我很难理解particles 之后的逗号在这一行中实现了什么:

particles, = ax.plot([], [], 'bo', ms=6)

如果省略逗号,脚本会出错,但语法(似乎类似于解包语句)对我来说没有多大意义,像

这样的语句
a, = [2,3]

失败,这似乎是反对拆包理论的论据。

任何见解将不胜感激。

【问题讨论】:

  • a, = [2] 有效,您只需要确保 RHS 是单个项目序列
  • “拆包理论”没有失败。请参阅下面我的回答,说a, _ = [2, 3] 有效。

标签: python tuples variable-assignment


【解决方案1】:

需要解包 1 元组(或任何其他长度为 1 的序列)。示例:

>>> a,b = (1,2)
>>> print a
1
>>> print b
2
>>> c, = (3,)
>>> print c
3
>>> d = (4,)
>>> print d
(4,)

注意 c 和 d 之间的区别。

注意:

a, = (1,2)

失败,因为您需要左侧的项目数量与右侧的可迭代项包含的项目数量相同。 Python 3.x 在某种程度上缓解了这种情况:

Python 3.2.3 (v3.2.3:3d0686d90f55, Apr 10 2012, 11:09:56) 
[GCC 4.0.1 (Apple Inc. build 5493)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> a,*rest = (1,2,3)
>>> a
1
>>> rest
[2, 3]

【讨论】:

  • 我尝试解包一个元组p, = (1,2),我得到“ValueError: too many values to unpack”,所以你的回答没有意义。请详细说明。
  • 确实如此。 p, = 期望有一个值来解包,据我所知,但您提供了 2 个值。谢谢@mgilson。
  • @Serdalis -- 要解压一个元组,你需要在左边有与元组包含的相同数量的元素。 (直到 python3 发明了a,*rest = tup 语法)。
  • @mgilson 非常正确,但您的回答是如此模棱两可,似乎说您可以使用该符号解压缩任何元组,但您已经修复了它,+1 来自我。
  • @Serdalis,否则,您可以在元组上使用常规切片/索引。
【解决方案2】:

为了教育起见,我会稍微长一点。

在 Python 中,元组用括号分隔,例如:(1, 2, 3)

不幸的是,如果简单地指定为 (1),则仅由单个元素(如 1)组成的元组将是模棱两可的(从解析的角度来看),因为这可能意味着 整数表达式中间的括号内。

为了克服这个问题,您可以指定一个只有一个元素的元组,该元素后跟一个逗号,如(1,)。 (为了清楚起见,comma 是使它成为元组的原因,而不是括号,当事情不模棱两可时我们可以省略它,这就是我在下面所做的)。毫无疑问,元组包含一个单一的 1 作为其元素,如下例所示:

>>> a = (1)
>>> a
1
>>> b = (1,)
>>> b
(1,)
>>> b[0]
1
>>> c, = b
>>> c
1
>>>

您提到的是一种“解包”元组的方法,即访问元组的特定元素。您使用的语法的另一种替代方法是用 0 对元组中的元素进行索引,例如上面示例中的 b[0]

对于具有多个元素的元组,您可以通过指定一个属性与元组具有的相同数量个元素来解压它们:

>>> x, y = (1, 2)
>>> x
1
>>> y
2

如果在解包元组时不使用相同数量的元素,则会抛出异常:

>>> z, = (1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack
>>> 

在解包理论“失败”的“示例”中,您可以简单地使用:

>>> a, _ = [2, 3]
>>> a
2

注意那里的_,这是 Python 中常用的变量,表示“我们不在乎”。

作为附录,请注意,在 a, _ = [2,3] 中,您正在从一个可变类型 list 隐式创建一个 tuple,它是一个不可变类型. (请注意,这种“隐式”转换是概念性的,因为 Python 解释器可能不会在字节码中生成 BUILD_TUPLE 指令)。请注意以下属性中的微妙之处:

>>> a, b = [2, 3]
>>> a
2
>>> b
3
>>> a, b
(2, 3)
>>> c, d = tuple([2, 3])
>>> c
2
>>> d
3
>>> e = [2, 3]
>>> type(e)
<type 'list'>
>>> type((a, b))
<type 'tuple'>

【讨论】:

  • 其实是逗号组成了元组。括号只是为了避免歧义。
  • AFAIK,这里没有隐式创建的元组。如果您检查字节码(使用dis.dis),指令很简单:UNPACK_SEQUENCESTORE_FAST(左侧每个对象一次)。
  • @mgilson,您在列表的情况下使用哪个代码?当我从元组案例(即a, b = [2, 3])中解压缩时,我确实得到了UNPACK_SEQUENCESTORE_FAST
  • 我的意思是那里没有BUILD_TUPLE 字节码(如果你做类似的事情:def foo(): a = (b,c))。换句话说,UNPACK_SEQUENCE 不会创建隐式元组。
  • 是的,当然,在你的情况下没有明确的BUILD_TUPLE(我确实在你的情况下得到)。
【解决方案3】:

看看plot 调用返回什么。在您的情况下,它是一个包含一个元素的列表:

>>> import matplotlib.pyplot as plt
>>> ax = plt.gca()
>>> ax.plot([], [], 'bo', ms=6)
[<matplotlib.lines.Line2D object at 0x353d6d0>]

现在,在这种情况下,使用 h, = ax.plot(...) 解包而不是像使用

h = ax.plot([], [], 'bo', ms=6)

后者将需要您稍后执行额外的步骤,例如

h[0].set_data(...)

plot 的返回值始终是一个列表,因为有时它需要返回多个 line 对象。甚至在列表中返回单行更有意义,这样客户端代码就不必以不同的方式处理每种情况。


您解包a, = [2,3] 失败的原因是右侧有两件事要解包,并且只有一个变量。你需要使用a,b = [2,3] 来解压它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-15
    • 1970-01-01
    • 2017-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-12
    • 2012-04-02
    相关资源
    最近更新 更多