¿Cómo ordenar una lista de objetos en función de un atributo de los objetos?

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: , , , ,  ...] 

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.

 # 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 »

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 

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 progtwig:

 #!/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.

 from operator import attrgetter ut.sort(key = attrgetter('count'), reverse = True) 

Enfoque orientado a objetos

Es una buena práctica hacer que la lógica de clasificación de objetos, si corresponde, una propiedad de la clase en lugar de incorporarse en cada instancia, se requiere la ordenación.

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 solo usa 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] 

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

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.