2012-09-27 11 views
7

Utilizo mucho las clases namedtuple. He estado pensando hoy si hay una buena manera de implementar la clasificación personalizada para dicha clase, es decir, hacer que la clave de clasificación predeterminada no sea el primer elemento (luego el segundo, el tercero, etc.) de namedtuple.Clasificación personalizada en una clase namedtuple

Mi primer instinto fue implementar __lt__ y __eq__ y dejar total_ordering haga el resto (que se llena a cabo 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 

Sin embargo:

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

oh, right ... total_ordering solo llena los otros métodos if they are missing. Como tuple/namedtuple tiene tales métodos, total_ordering no está haciendo nada por mí.

así que supongo que mis opciones son

  1. parada usando namedtuple y simplemente construir mi propia clase aburrida, siga usando total_ordering
  2. seguir usando namedtuple e implementar los 6 métodos de comparación
  3. seguir usando namedtuple y el inserto un valor de clasificación como el primer campo. Afortunadamente no tengo muchas instancias de la clase, pero generalmente solo confío en el orden de los campos para inicializarlos, lo que podría ser desagradable. Tal vez ese es un mal hábito.

Sugerencias sobre la mejor manera de solucionar esto?

+0

¿Por qué no acaba de crear la tabla de selección con los campos en el orden que desea ordenar? – BrenBarn

+0

No me di cuenta de que me gustaría ordenar/maximizar, etc. hasta que ya lo había creado y lo usé por un tiempo. Así que podría agregar un campo principal ahora para ser el campo de clasificación, pero podría ser un poco perturbador. – pfctdayelise

+1

Pero, ¿cómo estás usando el namedtuple?Lo bueno de namedtuple es que le permite acceder a los elementos por su nombre, por lo que puede cambiar su tilde nombrada para tener los campos en el orden correcto y no afectar su código, siempre y cuando tenga acceso a los campos por su nombre (que presumiblemente es haciendo, o bien, ¿por qué usar namedtuple?). – BrenBarn

Respuesta

10

OPCIÓN 1. Utilice un mixin y aplicar la total_ordering a que

@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 

OPCIÓN 2. Haga su propio decorador basado en total_ordering y simplemente use eso en su lugar

+1

+1, la solución mixin es agradable. – nneonneo

+0

¿Hay alguna diferencia significativa en la sobrecarga para la opción 1 frente a la opción 2? – Will

+0

+1. La opción 1 es una solución directa al problema de total_ordering solo rellenando los valores faltantes. @ Will La sobrecarga para la Opción 1 es casi cero (un paso adicional en el orden de resolución del método. JohnLaRooy, sugiero agregar \ _ \ _ ranuras \ _ \ _ =() a su Opción1 para restaurar la eficiencia de la memoria. –

1

Mi consejo sería crear su namedtuple con los campos en el otro que desea que sean ordenados por. Es posible que tenga que cambiar las partes de su código donde crea sus valores (por ejemplo, cambie someTuple("name", 24) a someTuple(24, "name"), pero generalmente los valores se crean en menos lugares de los que se usan, por lo tanto, esto no debería ser una gran oferta. evita la molestia de escribir todos los métodos de comparación, y como un bono que también evita la sobrecarga de rendimiento adicional de tener esos métodos de comparación personalizados llamadas todo el tiempo.

3

Si, como su pregunta implica, su interés se centra sólo en clasificación namedtuples por una clave alternativa, ¿por qué no usar el tipo/ordenados key discusión con la función 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)] 

se puede ir incluso más allá y defina su propia función de clasificación:

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

El campo de clasificación aún no existe, entonces o necesito agregarlo o usar un método cmp que vea 2 campos y haga un poco de lógica sobre ellos. (Mi código de ejemplo es demasiado simplificado) – pfctdayelise

Cuestiones relacionadas