不可变性不会赋予元组内的可变对象。所有不变性意味着您无法更改存储的特定对象 - 即,您无法重新分配items[0].set
。无论该变量的类型如何,此限制都是相同的——如果它是一个列表,doingitems[0].list = items[0].list + [1,2,3]
会失败(不能将它重新分配给新对象),但doingitems[0].list.extend([1,2,3])
会起作用。
这样想:如果您将代码更改为: new_item = N(i,R,8)
thennew_item.set
现在是 R 的别名(当您重新分配对象时,Python 不会复制对象)。如果元组赋予可变成员不变性,您希望R.remove(1)
做什么?由于它与 相同,因此new_item.set
您对其中一个所做的任何更改都将在另一个中可见。如果集合因为成为元组的成员而变得不可变,R.remove(1)
则会突然失败。Python 中的所有方法调用的工作或失败仅取决于对象,而不取决于变量 -R.remove(1)
并且new_item.set.remove(1)
必须以相同的方式运行。
这也意味着:
R = set(range(0,8))
for i in range(0,8):
items.append(N(i,R,8))
可能有一个微妙的错误。R
永远不会在这里重新分配,因此每个 namedtuple 都items
得到相同的集合。您可以通过注意这items[0].set is items[1].set
是 True 来确认这一点。所以,无论何时你改变它们中的任何一个 - 或 R - 修改都会出现在任何地方(它们只是同一个对象的不同名称)。
当您执行类似的操作时,通常会出现这个问题
a = [[]] * 3
a[0].append(2)
现在将是[[2], [2], [2]]
。解决这个一般问题有两种方法:
首先,在分配它时要非常小心地创建一个新的可变对象,除非您确实想要一个别名。在嵌套列表示例中,通常的解决方案是执行a = [[] for _ in range(3)]
. 对于元组中的集合,将行移动R = ...
到循环内部,以便将其重新分配给 each 的新集合namedtuple
。
第二种方法是使用不可变类型。制作R
a frozenset
,添加和删除元素的能力就消失了。