Parámetro __key__ para clases en Python

Tengo una matriz de vectores y quiero ordenarlos por longitud:

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

Dice: "tipos no ordenados: Vector() < Vector()" Quiere que cree lt, gt.. métodos. Pero quiero ordenar sin usar cmp. ¿Es posible?

preguntado el 28 de agosto de 12 a las 12:08

Tenga en cuenta que probablemente no debería usar __key__ como el nombre de uno de sus métodos. los nombres de métodos entre paréntesis 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 de python y su método podría causar un comportamiento bastante interesante (no deseado).) -

Además, si solo está buscando una buena clase de vector, es posible que desee investigar numpy. Proporciona "ndarray" 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 se obtendría con las propias. implementación. -

@mgilson parece que OP estaba esperando __key__ estar ya definido como parte del modelo de datos de Python, con un comportamiento análogo al key parámetro de palabra clave para list.sort. -

Sugerencia menor: __str__ no debería devolver una nueva línea \n, deja que eso sea manejado por print, join, etc. declaraciones/funciones. -

Además, es costumbre nombrar el primer argumento de los métodos self más bien que a (a menos que sea un @staticmethod o algo). No es obligatorio, pero cuando leí su código, me obligó a hacer una doble toma porque esa convención está muy arraigada en mi psique. -

3 Respuestas

Yo implementaría __lt__ y __eq__ y luego usar el functools.total_ordering decorador de clase para obtener el resto de los métodos de comparación.

Si no tiene sentido tener sus vectores ordenados así, siempre puede usar el key palabra clave para sort (o sorted para esa materia):

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

Respondido 28 ago 12, 12:08

Esto es claro, pero semánticamente un poco dudoso: los vectores no están totalmente ordenados por longitud ya que, por ejemplo, (0,1) y (1,0) son diferentes pero se compararían como iguales. - Katriel

@katrielalex -- Algo cierto. Eres libre de implementar __eq__ y __lt__ como mejor le parezca. podrías hacer __eq__ define como self.x == other.x and 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 usaría por defecto un key está ordenando, 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 para saber independientemente). - mgilson

Creo que esta es una buena idea, y creo que debería funcionar en base a la source. La pregunta es: ¿ total_ordering definir __gt__ de una manera que vuelve False cuando x y y ¿son iguales? Y parece que de la fuente. El resultado en este caso no será un pedido total, sino un pedido anticipado total; lo que es total aquí es realmente el relación, en lugar del orden. - remitente

Aunque mirando hacia atrás en esto, el problema que __eq__ devolverá verdadero para dos vectores diferentes. Probablemente no sea lo ideal... - remitente

total_ordering tiene trampas sutiles: regebro.wordpress.com/2010/12/13/… - Sr_y_Sra_D

Tienes dos variantes aquí: implementar __cmp__ Funcionar en Vector clase o realice la clasificación de esta manera:

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

Respondido 28 ago 12, 20:08

Yo sugeriría cambiar el nombre de la __key__ método para __len__, ya que el valor devuelto es, de hecho, la longitud del vector. Entonces usa a.sort(key=len), que muestra la intención mucho más claramente. - Lauritz V. Thaulow

¡@lazyr ciertamente no! __len__ es por 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). - Carlos Knechtel

@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 es 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 norm. - lvc

Asimismo, vale la pena señalar que __cmp__ desaparece en python 3.x. (que el OP podría estar usando) - mgilson

Los documentos de Python dicen que lt/le/gt/ge/eq/ne son

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

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

Respondido 28 ago 12, 12:08

Vale la pena señalar que __cmp__ ya no se usa en python 3.x - mgilson

@mgilson de hecho, y dada la print uso, es posible que OP ya esté usando Py3. - lvc

@Ivc: sí, y la sintaxis de "estilo antiguo" para la declaración de clase (aunque eso podría deberse a que el OP no sabe heredar siempre de object en pitón 2.x). - mgilson

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.