名前付きタプルのリストを使用しています。名前が付けられた各タプルにフィールドを追加したいと思います。 (namedtuple.attribute = 'foo'
のように)属性として参照するだけでそれができるようですが、フィールドのリストに追加されません。フィールドリストで何もしない場合、このようにしてはいけない理由はありますか?フィールドを追加するより良い方法はありますか?
>>> 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')
namedtuple(...)
はnew classを返すため、何をするかがわかります。実際にResultオブジェクトを取得するには、そのクラスをインスタンス化します。したがって、正しい方法は次のとおりです。
Result = namedtuple('Result', ['x', 'y'])
result = Result(5, 6)
また、これらのオブジェクトに属性を追加しても機能しないことがわかります。だから、あなたがそれをしてはいけない理由はそれが機能しないからです。クラスオブジェクトの悪用のみが機能し、これが恐ろしくて恐ろしい考えである理由を詳しく説明する必要がないことを願っています。
名前付きタプルに属性を追加できるかどうかに関係なく(必要なすべての属性を事前にリストした場合でも)、名前付きタプルオブジェクトは作成後に変更できないことに注意してください。タプルは不変です。そのため、何らかの理由で、または何らかの形でオブジェクトを作成後に変更する必要がある場合は、namedtuple
を使用できません。カスタムクラスを定義したほうがいいです(namedtuple
が追加するものの一部は、変更可能なオブジェクトに対しても意味がありません)。
ここでは、そのタイプのインスタンスではなく、名前付きタプルのtype
を変更していることに注意してください。この場合、おそらく古いタイプのフィールドを追加して、新しいタイプを作成する必要があります。
result = namedtuple('Result',result._fields+('point',))
例えば。:
>>> result = namedtuple('Result',['x','y'])
>>> result = namedtuple('Result',result._fields+('point',))
>>> result._fields
('x', 'y', 'point')
定義後、namedtuple
に新しいフィールドを追加することはできません。唯一の方法は、新しいテンプレートを作成し、新しいnamedtuple
インスタンスを作成することです。
分析
>>> from collections import namedtuple
>>> result = namedtuple('Result',['x','y'])
>>> result
<class '__main__.Result'>
result
はタプルではなく、タプルを作成するクラスです。
>>> result.x
<property object at 0x02B942A0>
次のような新しいタプルを作成します。
>>> p = result(1, 2)
>>> p
Result(x=1, y=2)
>>> p.x
1
値x
をp
に出力します。
>>> p.x = 5
Traceback (most recent call last):
File "<pyshell#10>", line 1, in <module>
p.x = 5
AttributeError: can't set attribute
タプルは不変であるため、これはエラーをスローします。
>>> result.x = 5
>>> result
<class '__main__.Result'>
>>> result._fields
('x', 'y')
>>> p = result(1, 2)
>>> p
Result(x=1, y=2)
これは何も変更しません。
>>> result.description = 'point'
>>> result
<class '__main__.Result'>
>>> result._fields
('x', 'y')
これでも何も変わりません。
ソリューション
>>> 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)
名前付きタプルは不変であることを念頭に置いて、簡単に連結できます。
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)