Python list of named tuples, replacing attributes

scriptOmate picture scriptOmate · Jan 25, 2012 · Viewed 9.3k times · Source

Here is some simplified code that I don't understand why it does not work.

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)

Here is the output

'>>>
[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)]'

Answer

Rob Wouters picture Rob Wouters · Jan 25, 2012

_replace does not do what you think it does. From the docs:

somenamedtuple._replace(kwargs)

Return a new instance of the named tuple replacing specified fields with new values:

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

You're calling _replace but you never store the new Node it returns. The reason it returns a new object instead of altering the object 'in-place' is that namedtuples are by definition immutable, i.e. they can't be altered after you created them.

Be aware your for loop creates a list of four references to the same Node. In this case this isn't really a problem since the objects you are creating are all the same and namedtuple is immutable, but in general be aware of this.

So in summary:

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)