0

我知道python中的namedtuple是不可变的,它的属性值不能直接重新分配

N = namedtuple("N",['ind','set','v'])
def solve()
    items=[]
    R = set(range(0,8))
    for i in range(0,8):
        items.append(N(i,R,8))  
    items[0].set.remove(1)
    items[0].v+=1

最后就像我为属性'v'分配一个新值的地方不起作用。但是从 items[0] 的 set属性中删除元素“ 1”是可行的。

如果 set 属性是 List 类型,为什么会这样?

4

2 回答 2

0

你改变集合,而不是元组。集合是可变的。

>>> s = set()
>>> t = (s,)
>>> l = [s]
>>> d = {42: s}
>>> t
(set([]),)
>>> l
[set([])]
>>> d
{42: set([])}
>>> s.add('foo')
>>> t
(set(['foo']),)
>>> l
[set(['foo'])]
>>> d
{42: set(['foo'])}
于 2014-03-22T08:29:52.603 回答
0

不可变性不会赋予元组内的可变对象。所有不变性意味着您无法更改存储的特定对象 - 即,您无法重新分配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

第二种方法是使用不可变类型。制作Ra frozenset,添加和删除元素的能力就消失了。

于 2014-03-22T08:30:16.463 回答