4

有人可以在 Python 中澄清这两个符号吗:

  • [ [] ] * n:显然这会创建对同一对象的 n 引用(在这种情况下,是一个空列表)。这在哪些情况下有用?
  • (i,):我见过有人使用这种“尾随逗号”表示法(例如:在函数的定义中生成 {0, 1, 2, ... n-1} 的所有大小 k 个子集sets)。这是什么意思?
4

4 回答 4

10

是的,这确实会创建对同一对象的引用:

>>> L = [[]] * 3
>>> L
[[], [], []]
>>> L[0].append(1)
>>> L
[[1], [1], [1]]
>>> map(id, L)
[4299803320, 4299803320, 4299803320]

当您想要创建具有相同项目的对象时,这可能很有用。


(i,)使用 item 创建一个元组i

>>> mytuple = (5,)
>>> print mytuple
(5,)
>>> print type(mytuple)
<type 'tuple'>

需要逗号的原因是因为否则它将被视为整数:

>>> mytuple = (5)
>>> print mytuple
5
>>> print type(mytuple)
<type 'int'>
于 2013-10-15T07:00:56.637 回答
3

[ [] ] * n创建n对同一个空列表对象的引用。这几乎总是一个错误,因为通常您最终会在其中一个n列表上使用破坏性操作,然后当它应用于外部列表的“所有”元素时会感到惊讶。

但是[0] * n会创建n对相同整数零对象的引用。像这样的表达式可以很方便地初始化一个计数器列表(您可能会继续替换列表中的各个插槽)。由于整数是不可变的,因此不会有意外更改“所有”零的危险。任何其他“自始至终不可变”类型也是如此(不一定是元组,因为尽管元组本身是不可变的,但它可以包含可变值)。

所以这个“列表乘法”操作并不是没用的。但我建议避免使用它,除非复制一个有保证的不可变值列表的特定情况。虽然可以安全地使用可变值列表相乘,但它非常脆弱,而且经常是错误的根本原因,即使这是你的意思,你最好以稍微更冗长的方式来做它很明显你是认真的。


至于(i,),Python 允许在列表、字典、元组和集合文字中使用“额外”逗号。例如,以下都是有效的:

(1, 2, 3,)
[1, 2, 3,]
{1: 'one', 2: 'two', 3: 'three',}
{1, 2, 3,}

这与省略尾随逗号没有什么不同,它只是允许作为可选的额外内容。

回到(i,)。这只是一个带有尾随逗号的项目的元组。但是有一个问题;对于只有一个项目的元组的特殊情况,尾随逗号不是可选的额外内容,它是强制性的。那是因为否则会写一个“单元组” (i),但是这种句法形式已经用于使用括号对子表达式进行分组(例如,您无法判断是否(1 + 1) * 2应该产生4or (2, 2))。


顺便说一句,当您将长元组/列表/字典/集拆分为多行时,有时可能要使用可选尾随逗号的原因更为明显:

foo = [
   'this',
   'is',
   'a',
   'long',
   'list',
]

以这种风格写作允许我通过重新排序源代码行来重新排序列表;如果我重新订购的物品之一是列表中的最后一件,我就不必去清理逗号。

同样,如果我使用版本控制,在最后一项之后添加更多项不会改变与先前最终项的行,因此差异是纯粹的添加。如果最后一行没有逗号,则需要添加一个逗号以添加更多项目,这会使更改记录为对该行的编辑,这使得合并冲突(稍微)更有可能并且历史日志(稍微)更难读。

于 2013-10-15T07:29:49.923 回答
0

到目前为止,最常见的用途[...] * n必须是石斑鱼食谱

def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
    args = [iter(iterable)] * n
    return izip_longest(fillvalue=fillvalue, *args)
于 2013-10-15T09:17:01.613 回答
0

[[]] * n 创建对同一个可变列表的引用。很多时候,这不是我们所需要的,而是您需要可以单独修改的唯一列表对象。为此,您可以使用以下命令:

list_of_lists = [[] for i in xrange(n)]

于 2013-10-15T08:17:29.920 回答