2012-08-28 38 views
6

Tengo una gran variedad de vectores y quiero ordenarlos por longitud:__key__ parámetro para las clases en Python

class Vector: 

    def __init__(self, x, y): 
     self.x, self.y = x, y 

    def __add__(a, b): 
     return Vector(a.x + b.x, a.y + b.y) 

    def __str__(a): 
     return str(a.x) + ' ' + str(a.y) + '\n' 

    def __key__(self): 
     return self.x * self.x + self.y * self.y 


a = [] 
a.append(Vector(1,2)) 
a.append(Vector(1, 1)) 
a.sort() 
print("".join(map(str,a))) 

Se dice: "tipos unorderable: Vector() < vectorial()" Me quiere para crear lt, gt .. métodos. Pero quiero ordenar sin usar cmp. ¿Es posible?

+3

Tenga en cuenta que es probable que no debe utilizar '__key__' como el nombre de uno de sus métodos. los nombres de métodos entre corchetes por pares de guiones bajos están reservados para medios especiales por la especificación (Esto significa que en cualquier momento, Guido podría decidir que quiere agregar '__key__' como parte del modelo de datos python y su método podría causar algunos bastante interesantes (no deseados)) comportamiento.) – mgilson

+0

Además, si solo está buscando una clase de vector agradable, es posible que desee buscar 'numpy'. Proporciona "ndarray" s que se comportan de manera muy similar a los vectores, admite una forma de indexación muy interesante (y útil) y realiza todas las operaciones matemáticas en C, lo que las hace un poco más rápidas de lo que obtendría al hacer su propio cálculo implementación. – mgilson

+1

@mgilson parece que OP esperaba que '__key__' ya se definiera como parte del modelo de datos de Python, con un comportamiento análogo al parámetro de palabra clave' key' de 'list.sort'. –

Respuesta

2

Python docs says que LT/le/GT/ge/eq/ne son

[...] los llamados métodos “rico comparación”, y se llaman para operadores de comparación con preferencia a __cmp__()

Si implementa un método __cmp__(self, other), debe usarse para operaciones de comparación/ordenación.

+3

Vale la pena señalar que '__cmp__' ya no se usa en Python 3.x – mgilson

+0

@mgilson, y dado el uso de' print', es posible que OP ya esté usando Py3. – lvc

+0

@Ivc - Sí, y sintaxis de "estilo antiguo" para la declaración de clase (aunque eso podría ser simplemente porque el OP no sabe heredar siempre de 'object' en python 2.x). – mgilson

6

Implementaré __lt__ y __eq__ y luego usaré el decorador de clases functools.total_ordering para obtener el resto de los métodos de comparación.

Si no tiene sentido tener sus vectores ordenados así, entonces siempre puede utilizar la palabra clave key a sort (o sorted para el caso):

mylist.sort(key = lambda v: v.x**2 + v.y**2) 
+0

Esto es limpio, pero semánticamente un poco dudoso: los vectores no están realmente totalmente ordenados por longitud ya que, por ejemplo, (0,1) y (1,0) son diferentes pero se compararían igual. – katrielalex

+0

@katrielalex - Algo cierto. Eres libre de implementar '__eq__' y' __lt__' como mejor te parezca.Podría hacer '__eq__' definido como' self.x == other.x y self.y == other.y' por ejemplo. En este caso particular, realmente no veo cómo un vector puede ser "menor que" otro, así que en este caso, probablemente utilizaría una 'clave 'para ordenar, pero pensé que incluiría' functools .total_ordering' ya que se parece más a lo que parece que OP está tratando de hacer. (También es un decorador útil saber independientemente). – mgilson

+0

Creo que esta es una buena idea, y creo que debería funcionar basada en ['source'] (http://hg.python.org/cpython/file/2.7/Lib/functools.py#l53). La pregunta es: ¿'total_ordering' define' __gt__' de una manera que devuelve 'False' cuando' x' y 'y' son iguales? Y parece que proviene de la fuente. El resultado en este caso no será un pedido total, sino un [preorden total] (http://en.wikipedia.org/wiki/Strict_weak_ordering#Total_preorders); lo que es total aquí es realmente la _relación_, más que el orden. – senderle

6

Tiene dos variantes aquí : aplicar __cmp__ función en Vector clase o realizar la clasificación de esta manera:

... 
a.sort(key=Vector.__key__) 
+1

Sugeriría cambiar el nombre del método '__key__' a' __len__', ya que el valor devuelto es de hecho la longitud del vector. Luego use 'a.sort (key = len)', que muestra la intención mucho más claramente. –

+4

@lazyr ciertamente no! '__len__' es para la longitud * de secuencias *; Python espera que devuelva un número entero. (Además, el valor que se devuelve actualmente es el cuadrado de la longitud). –

+2

@lazyr - Estoy de acuerdo con el principio de que '__key__' no es el mejor nombre (incluso aparte de que posiblemente signifique algo específico en el futuro, no lo es). 't muy específico), pero '__len__' es un poco cuestionable, ya que' help (len) 'dice que es" el número de elementos de una secuencia o mapeo ". Probablemente lo definiría como una función separada (no un método) llamada algo así como 'norma '. – lvc

Cuestiones relacionadas