Pregunta ¿Cómo ordenar una lista de objetos basada en un atributo de los objetos?


Tengo una lista de objetos de Python que me gustaría ordenar por un atributo de los objetos mismos. La lista se ve así:

>>> 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 conteos descendientes.

He visto varios métodos para esto, pero estoy buscando las mejores prácticas en Python.


533
2017-12-31 16:41


origen


Respuestas:


# 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 en clasificación por claves » 


914
2017-12-31 16:42



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 previa al operador de Python, por lo que sería bueno tener un mecanismo de respaldo. Es posible que desee hacer lo siguiente, luego:

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

60
2017-12-31 17:48



Los lectores deben notar que el método clave =

ut.sort(key=lambda x: x.count, reverse=True)

es mucho 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"). Puede confirmarlo 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 más de 10 veces más lento, pero el libro dice que en general es 5 veces más lento. La razón por la que dicen se debe al algoritmo de clasificación altamente optimizado utilizado en python (timsort)

Aún así, es muy extraño que .sort (lambda) sea más rápido que simple .sort (). Espero que arreglen eso.


52
2018-06-12 19:54



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

26
2017-12-31 19:00



Se parece mucho a una lista de instancias de modelo de ORM de Django.

¿Por qué no ordenarlos en una consulta como esta:

ut = Tag.objects.order_by('-count')

13
2017-12-31 17:10



Enfoque orientado a objetos

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

Esto garantiza la coherencia y elimina la necesidad de un código repetitivo.

Como mínimo, debe especificar __eq__ y __lt__ operaciones para que esto funcione Entonces 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]

11
2018-02-11 11:35



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.


Actualizar: Aunque este método funcionaría, creo que la solución de Triptych es más adecuada para su caso, porque es mucho más simple.


9
2017-12-31 16:45