问题
I am working with a list of namedtuples. I would like to add a field to each named tuple after it has already been created. It seems I can do that by just referencing it as an attribute (as in namedtuple.attribute = 'foo'
), but then it isn't added to the list of fields. Is there any reason why I shouldn't do it this way if I don't do anything with the fields list? Is there a better way to add a field?
>>> from collections import namedtuple
>>> result = namedtuple('Result',['x','y'])
>>> result.x = 5
>>> result.y = 6
>>> (result.x, result.y)
(5, 6)
>>> result.description = 'point'
>>> (result.x, result.y, result.description)
(5, 6, 'point')
>>> result._fields
('x', 'y')
回答1:
What you do works because namedtuple(...) returns a new class. To actually get a Result object, you instantiate that class. So the correct way is:
Result = namedtuple('Result', ['x', 'y'])
result = Result(5, 6)
And you'll find that adding attributes to these objects does not work. So the reason you shouldn't do it is because it doesn't work. Only abusing the class objects works, and I hope I don't need to go into detail why this is a horrible, horrible idea.
Note that regardless of whether you can add attributes to namedtuples or not (and even if you list all attributes you need beforehand), you cannot change a namedtuple object after it's created. Tuples are immutable. So if you need to change objects after creation for any reason, in any way or shape, you can't use namedtuple
. You're better off defining a custom class (some of the stuff namedtuple
adds for you doesn't even make sense for mutable objects).
回答2:
Notice that here you're modifying the type
of the named tuples, not instances of that type. In this case, you'd probably want to create a new type with an additional field from the old one:
result = namedtuple('Result',result._fields+('point',))
e.g.:
>>> result = namedtuple('Result',['x','y'])
>>> result = namedtuple('Result',result._fields+('point',))
>>> result._fields
('x', 'y', 'point')
回答3:
You can easily concatenate namedtuples, keeping in mind that they are immutable
from collections import namedtuple
T1 = namedtuple('T1', 'a,b')
T2 = namedtuple('T2', 'c,d')
t1 = T1(1,2)
t2 = T2(3,4)
def sum_nt_classes(*args):
return namedtuple('_', ' '.join(sum(map(lambda t:t._fields, args), ())))
def sum_nt_instances(*args):
return sum_nt_classes(*args)(*sum(args,()))
print sum_nt_classes(T1,T2)(5,6,7,8)
print sum_nt_instances(t1,t2)
回答4:
You cannot add a new field to a namedtuple
after defining it. Only way is to create a new template and creating new namedtuple
instances.
Analysis
>>> from collections import namedtuple
>>> result = namedtuple('Result',['x','y'])
>>> result
<class '__main__.Result'>
result
is not a tuple, but the class which creates tuples.
>>> result.x
<property object at 0x02B942A0>
You create a new tuple like this:
>>> p = result(1, 2)
>>> p
Result(x=1, y=2)
>>> p.x
1
Prints the value x
in p
.
>>> p.x = 5
Traceback (most recent call last):
File "<pyshell#10>", line 1, in <module>
p.x = 5
AttributeError: can't set attribute
This throws error because tuple is immutable.
>>> result.x = 5
>>> result
<class '__main__.Result'>
>>> result._fields
('x', 'y')
>>> p = result(1, 2)
>>> p
Result(x=1, y=2)
This doesn't change anything.
>>> result.description = 'point'
>>> result
<class '__main__.Result'>
>>> result._fields
('x', 'y')
This doesn't change anything either.
Solution
>>> result = namedtuple('Result', ['x','y'])
>>> p = result(1, 2)
>>> p
Result(x=1, y=2)
>>> # I need one more field
>>> result = namedtuple('Result',['x','y','z'])
>>> p1 = result(1, 2, 3)
>>> p1
Result(x=1, y=2, z=3)
>>> p
Result(x=1, y=2)
来源:https://stackoverflow.com/questions/15324644/how-do-i-add-fields-to-a-namedtuple