how do I add fields to a namedtuple?

爱⌒轻易说出口 提交于 2021-02-07 11:16:57

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!