2012-09-27 7 views
7

Uso molto le classi namedtuple. Oggi ho pensato se c'è un buon modo per implementare l'ordinamento personalizzato per una classe del genere, cioè rendere la chiave di ordinamento predefinita non il primo elemento (quindi il secondo, il terzo, ecc.) Della namedtuple.Ordinamento personalizzato su una classe namedtuple

Il mio primo istinto è stato quello di implementare __lt__ e __eq__ e lasciare total_ordering fanno il resto (che riempie le, NE, GT, GE):

from collections import namedtuple 
from functools import total_ordering 


@total_ordering 
class B(namedtuple('B', 'x y')): 
    def __lt__(self, other): 
     return self.y < other.y 

Tuttavia:

def test_sortingB(): 
    b1 = B(1, 2) 
    b2 = B(2, 1) 
    assert b2 < b1 # passes 
    assert b2 <= b1 # fails 

oh, a destra ... total_ordering compila solo gli altri metodi if they are missing. Dato che tuple/namedtuple ha metodi simili, total_ordering non sta facendo nulla per me.

Quindi credo che le mie opzioni sono

  1. arresto utilizzando namedtuple e solo costruire il mio classe noiosa, continuare ad usare total_ordering
  2. continuare ad usare namedtuple e implementare tutti i 6 metodi di confronto
  3. continuare ad usare namedtuple ed inserto un valore di ordinamento come primo campo. Fortunatamente non ho troppe istanze della classe, ma di solito mi baso sull'ordine dei campi per inizializzarli che potrebbero essere cattivi. Forse è una cattiva abitudine.

Suggerimenti sul modo migliore per risolvere questo?

+0

Perché non si crea semplicemente il namedtuple con i campi nell'ordine in cui si desidera ordinare? – BrenBarn

+0

Non mi sono reso conto che avrei voluto smistare/max fino a quando non l'avessi già creato e usato per un po '. Quindi potrei aggiungere un campo guida ora per essere il campo di ordinamento, ma potrebbe essere un po 'dirompente. – pfctdayelise

+1

Ma come stai usando il namedtuple?La cosa bella di namedtuple è che ti permette di accedere agli elementi per nome, in modo da poter cambiare il tuo namedtuple per avere i campi nel giusto ordine e non influenzare il tuo codice, a patto che tu acceda ai campi per nome (che presumibilmente sei fare, altrimenti perché usato namedtuple?). – BrenBarn

risposta

10

OPZIONE 1. Usare un mixin e applicare il total_ordering a quella

@total_ordering 
class B_ordering(object): 
    __slots__ =()     # see Raymond's comment 
    def __lt__(self, other): 
     return self.y < other.y 

class B(B_ordering, namedtuple('B', 'x y')): 
    pass 

OPZIONE 2. Crea il tuo decoratore basato su total_ordering e usa quello invece

+1

+1, la soluzione di mixin è buona. – nneonneo

+0

C'è qualche differenza significativa nell'overhead per l'opzione 1 rispetto all'opzione 2? – Will

+0

+1. L'opzione 1 è una soluzione diretta al problema della total_ordering che riempie solo i valori mancanti. @ Will L'overhead per l'Opzione 1 è quasi zero (un passo in più nell'ordine di risoluzione dei metodi. @JohnLaRooy, suggerisco di aggiungere \ _ \ _ slot \ _ \ _ =() all'Opzione 1 per ripristinare l'efficienza della memoria. –

1

Il mio consiglio è di creare il tuo namedtuple con i campi nell'altro da cui vuoi che siano ordinati. Potrebbe essere necessario modificare le parti del codice in cui si creano i valori (ad esempio, modificare someTuple("name", 24) in someTuple(24, "name"), ma in genere i valori vengono creati in un numero di posti inferiore rispetto a quello in cui vengono utilizzati, quindi non dovrebbe essere un grosso problema. evita il fastidio di scrivere tutti i metodi di confronto, e come bonus evita anche l'overhead prestazioni aggiuntive di avere quei metodi di confronto personalizzato chiamato per tutto il tempo.

3

Se, come suggerisce la tua domanda, il vostro interesse è solo in ordinamento namedtuples da una chiave alternativa, perché non utilizzare il tipo/ordinato key discussione con la funzione attrgetter:

>>> from collections import namedtuple 
>>> from operator import attrgetter 
>>> P = namedtuple("P", "x y") 
>>> p1 = P(1, 2) 
>>> p2 = P(2, 1) 
>>> sorted([p1, p2], key=attrgetter("y")) 
[P(x=2, y=1), P(x=1, y=2)] 

Si può andare anche ulteriormente e definire la propria funzione di ordinamento:

>>> from functools import partial 
>>> sortony = partial(sorted, key=attrgetter("y")) 
>>> sortony([p1, p2]) 
[P(x=2, y=1), P(x=1, y=2)] 
+0

Il campo di ordinamento non esiste ancora, quindi ho bisogno di aggiungerlo o usare un metodo cmp che guarda 2 campi e fa un po 'di logica su di loro. (Il mio codice di esempio è eccessivamente semplificato) – pfctdayelise