2013-03-10 11 views
10

Sto lavorando con un elenco di namedtuples. Vorrei aggiungere un campo a ciascuna tupla con nome dopo che è stata già creata. Sembra che posso farlo semplicemente facendo riferimento come attributo (come in namedtuple.attribute = 'foo'), ma poi non viene aggiunto all'elenco dei campi. C'è qualche ragione per cui non dovrei farlo in questo modo se non faccio nulla con la lista dei campi? C'è un modo migliore per aggiungere un campo?come aggiungere campi a un namedtuple?

>>> 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') 
+0

E perché non usi semplicemente dict? dict.keys di sicuro avrà aggiunto "campi". – omikron

risposta

10

Quello che fai opere perché namedtuple(...) restituisce una nuova classe. Per ottenere effettivamente un oggetto Risultato, si crea un'istanza di quella classe. Quindi il modo corretto è:

Result = namedtuple('Result', ['x', 'y']) 
result = Result(5, 6) 

E vi accorgerete che l'aggiunta di attributi a questi oggetti non non lavoro. Quindi la ragione per cui non dovresti farlo è perché non funziona. Solo abusare degli oggetti di classe funziona, e spero di non aver bisogno di entrare nei dettagli perché questa è un'idea orribile e orribile.

Si noti che indipendentemente dal fatto che sia possibile aggiungere attributi a namedtuples o meno (e anche se si elencano tutti gli attributi necessari in precedenza), non è possibile modificare un oggetto con nome una volta creato. Le tuple sono immutabili. Pertanto, se è necessario modificare gli oggetti dopo la creazione per qualsiasi motivo, in qualsiasi modo o forma, non è possibile utilizzare namedtuple. È meglio definire una classe personalizzata (alcuni degli additivi namedtuple per te non hanno senso per gli oggetti mutabili).

2

Non è possibile aggiungere un nuovo campo a un namedtuple dopo aver definito essa. L'unico modo è creare un nuovo modello e creare nuove istanze namedtuple.

Analisi

>>> from collections import namedtuple 
>>> result = namedtuple('Result',['x','y']) 
>>> result 
<class '__main__.Result'> 

result non è una tupla, ma la classe che crea tuple.

>>> result.x 
<property object at 0x02B942A0> 

si crea una nuova tupla come questo:

>>> p = result(1, 2) 
>>> p 
Result(x=1, y=2) 

>>> p.x 
1 

stampa il valore 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 

Questo genera errore perché tupla è immutabile.

>>> result.x = 5 
>>> result 
<class '__main__.Result'> 
>>> result._fields 
('x', 'y') 
>>> p = result(1, 2) 
>>> p 
Result(x=1, y=2) 

Questo non cambia nulla.

>>> result.description = 'point' 
>>> result 
<class '__main__.Result'> 
>>> result._fields 
('x', 'y') 

Anche questo non cambia nulla.

Soluzione

>>> 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) 
9

Si noti che qui si sta modificando lo type delle tuple denominate, non le istanze di quel tipo.In questo caso, si sarebbe probabilmente desidera creare un nuovo tipo con un ulteriore campo da quello vecchio:

result = namedtuple('Result',result._fields+('point',)) 

esempio:

>>> result = namedtuple('Result',['x','y']) 
>>> result = namedtuple('Result',result._fields+('point',)) 
>>> result._fields 
('x', 'y', 'point') 
0

Si può facilmente concatenare namedtuples, tenendo presente che essi sono immutabili

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) 
Problemi correlati