python - poo - ¿Cómo ordenar una lista de objetos en función de un atributo de los objetos?
python poo tutorial (7)
Agregue operadores de comparación ricos a la clase de objeto, luego use el método sort () de la lista.
Ver comparación rica en python .
Actualización : aunque este método funcionaría, creo que la solución de Triptych se adapta mejor a su caso porque es mucho más simple.
Tengo una lista de objetos de Python que me gustaría ordenar por un atributo de los objetos en sí. La lista se ve como:
>>> ut
[<Tag: 128>, <Tag: 2008>, <Tag: <>, <Tag: actionscript>, <Tag: addresses>,
<Tag: aes>, <Tag: ajax> ...]
Cada objeto tiene un conteo:
>>> ut[1].count
1L
Necesito ordenar la lista por número de cuentas descendente.
He visto varios métodos para esto, pero estoy buscando las mejores prácticas en Python.
Los lectores deben notar que la clave = método:
ut.sort(key=lambda x: x.count, reverse=True)
es muchas veces más rápido que agregar operadores de comparación ricos a los objetos. Me sorprendió leer esto (página 485 de "Python in a Nutshell"). Puedes confirmar esto ejecutando pruebas en este pequeño programa:
#!/usr/bin/env python
import random
class C:
def __init__(self,count):
self.count = count
def __cmp__(self,other):
return cmp(self.count,other.count)
longList = [C(random.random()) for i in xrange(1000000)] #about 6.1 secs
longList2 = longList[:]
longList.sort() #about 52 - 6.1 = 46 secs
longList2.sort(key = lambda c: c.count) #about 9 - 6.1 = 3 secs
Mis pruebas, muy mínimas, muestran que el primer tipo es 10 veces más lento, pero el libro dice que solo es 5 veces más lento en general. La razón por la que dicen se debe al algoritmo de ordenamiento altamente optimizado utilizado en python ( timsort ).
Aún así, es muy extraño que .sort (lambda) sea más rápido que el antiguo .sort (). Espero que solucionen eso.
Se parece mucho a una lista de instancias del modelo Django ORM.
¿Por qué no ordenarlos en una consulta como esta?
ut = Tag.objects.order_by(''-count'')
Una forma que puede ser más rápida, especialmente si su lista tiene muchos registros, es usar operator.attrgetter("count")
. Sin embargo, esto podría ejecutarse en una versión preoperatoria de Python, por lo que sería bueno tener un mecanismo de respaldo. Es posible que desee hacer lo siguiente, entonces:
try: import operator
except ImportError: keyfun= lambda x: x.count # use a lambda if no operator module
else: keyfun= operator.attrgetter("count") # use operator since it''s faster than lambda
ut.sort(key=keyfun, reverse=True) # sort in-place
Enfoque orientado a objetos
Es una buena práctica hacer que la lógica de ordenación de objetos, si corresponde, una propiedad de la clase en lugar de incorporarse en cada instancia, se requiere el ordenamiento.
Esto asegura la consistencia y elimina la necesidad de código repetitivo.
Como mínimo, debe especificar las operaciones __eq__
y __lt__
para que esto funcione. Luego simplemente use sorted(list_of_objects)
.
class Card(object):
def __init__(self, rank, suit):
self.rank = rank
self.suit = suit
def __eq__(self, other):
return self.rank == other.rank and self.suit == other.suit
def __lt__(self, other):
return self.rank < other.rank
hand = [Card(10, ''H''), Card(2, ''h''), Card(12, ''h''), Card(13, ''h''), Card(14, ''h'')]
hand_order = [c.rank for c in hand] # [10, 2, 12, 13, 14]
hand_sorted = sorted(hand)
hand_sorted_order = [c.rank for c in hand_sorted] # [2, 10, 12, 13, 14]
# To sort the list in place...
ut.sort(key=lambda x: x.count, reverse=True)
# To return a new list, use the sorted() built-in function...
newlist = sorted(ut, key=lambda x: x.count, reverse=True)
Más sobre clasificación por llaves »
from operator import attrgetter
ut.sort(key = attrgetter(''count''), reverse = True)