I am modeling database records using collections.namedtuple
. At times, I want the user to be able to replace the contents of an arbitrary field. The _replace()
method permits replacing the contents of a specific field as long as we can specify its name as part of the argument: somenamedtuple._replace(somefield=newcontent)
. But if the name itself is to be dynamically supplied by the user, I am unable to find a way to do it.
Here is a minimal working example:
from collections import namedtuple
fields = ['one', 'two', 'three']
Record = namedtuple('Record', fields)
# Populate fields.
record = Record(*tuple(['empty' for i in fields]))
while True:
# Show what we have already.
print('0: quit')
for i in range(len(fields)):
print('{}: {}: {}'.format(i+1, fields[i], record[i]))
to_change = int(input('Field to change: '))
if not to_change:
break
else:
new_content = input('New content: ')
field_to_change = {fields[to_change-1]:new_content}
print('Setting', field_to_change)
record._replace(**field_to_change)
print('Finished.')
print(record)
Output (Ipython 1.0.0, Python 3.3.1) follows.
In [1]: run test_setattr_namedtuple
0: quit
1: one: empty
2: two: empty
3: three: empty
Field to set: 2
New content: asdf
Setting {'two': 'asdf'}
0: quit
1: one: empty
2: two: empty
3: three: empty
Field to set: 0
Finished.
Record(one='empty', two='empty', three='empty')
In [2]:
The record._replace()
line is attempting to set 'two' to 'asdf', rather than two
and so fails silently. I had thought of using eval
inside _replace()
, but _replace()
does not accept expressions as arguments.
I also tried the built-in function setattr
, but it doesn't work with namedtuples, presumably because they are immutable.
The
._replace()
method returns the altered named tuple instance. You are discarding the returned value.Like tuples, namedtuple-derived class instances are immutable, and
._replace()
does not change the value in-place.Replace the original value with the new: