Python命名元组列表,替换属性

5
这里有一些简化的代码,我不明白为什么它不能运行。
from collections import namedtuple

MyStruct = namedtuple('MyStruct', 'ThreadInstance ThreadName Mnemonic IpAddr IpGW Status Mode')

Node = MyStruct(None, '', '', '', '',  -1, 0)
NodeDb = []
for id in range(4):
    NodeDb.append(Node)

NodeDb[2]._replace(ThreadName='T2')
NodeDb[2]._replace(Mnemonic='ABCD')
NodeDb[2]._replace(IpAddr='192.0.1.2')
NodeDb[2]._replace(IpGW='192.0.1.3')
NodeDb[2]._replace(Status=0)
NodeDb[2]._replace(Mode=2)

print(NodeDb)

这是输出结果

'>>>
[MyStruct(ThreadInstance=None, ThreadName='', Mnemonic='', IpAddr='', IpGW='', Status=-1, Mode=0),
 MyStruct(ThreadInstance=None, ThreadName='', Mnemonic='', IpAddr='', IpGW='', Status=-1, Mode=0), 
 MyStruct(ThreadInstance=None, ThreadName='', Mnemonic='', IpAddr='', IpGW='', Status=-1, Mode=0), 
 MyStruct(ThreadInstance=None, ThreadName='', Mnemonic='', IpAddr='', IpGW='', Status=-1, Mode=0)]'

2
你期望的输出是什么? - Ken Kinder
-1:标题糟糕透顶。毫无意义,没有帮助。 - S.Lott
2个回答

7

_replace 并不是你想象中的那样。来自文档:

somenamedtuple._replace(kwargs)

返回一个新的命名元组实例,用新值替换指定字段:

>>> p = Point(x=11, y=22)  
>>> p._replace(x=33)  
Point(x=33, y=22)

您正在调用_replace,但您从未存储它返回的新Node。它返回一个新对象而不是直接修改原始对象的原因是,namedtuples在定义上是不可变的,即创建后无法更改。
请注意,您的for循环创建了对同一Node的四个引用的列表。在这种情况下,由于您创建的所有对象都是相同的,并且namedtuple是不可变的,因此这并不是真正的问题,但总体上要注意这一点。
所以,总之:
from collections import namedtuple

MyStruct = namedtuple('MyStruct', 'ThreadInstance ThreadName Mnemonic IpAddr IpGW Status Mode')

NodeDb = []
Node = MyStruct(None, '', '', '', '',  -1, 0)
for id in range(4):
    NodeDb.append(Node)

NodeDb[2] = NodeDb[2]._replace(ThreadName='T2',
                               Mnemonic='ABCD',
                               IpAddr='192.0.1.2',
                               IpGW='192.0.1.3',
                               Status=0,
                               Mode=2)

print(NodeDb)

1
非常感谢。现在你指出_replace是一个新对象,我明白了。 - scriptOmate
@scriptOmate,不用谢。如果我的回答确实解决了你的问题,你可以将其标记为已解决 :) - Rob Wouters

1
请记住,列表中的每个项目都像C语言术语中的指针一样工作。Python变量始终通过引用传递和使用。也就是说,按照它的写法,这是正确的:
assert NodeDb[0] is NodeDb[1] is NodeDb[2] is NodeDb[3] # True

我认为你想要一个单独对象的列表,如果是这样的话,你应该将 Node = MyStruct(...) 移到你的 for 循环内部。


总的来说,你是正确的,但在这里并不重要,因为所有对象都是相同的且不可变的。事实上,创建四个相同的(命名)元组实例是一种浪费。 - Rob Wouters

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接