¿Cómo clasifico una lista de objetos según un atributo de los objetos?

6 minutos de lectura

Avatar de usuario de Nick Sergeant
nick sargento

Tengo una lista de objetos de Python que quiero ordenar por un atributo específico de cada objeto:

[Tag(name="toe", count=10), Tag(name="leg", count=2), ...]

¿Cómo ordeno la lista por .count ¿en orden descendente?

  • Duplicado: stackoverflow.com/questions/157424/…, stackoverflow.com/questions/222752/…, stackoverflow.com/questions/327191/…

    – S. Lott

    31 de diciembre de 2008 a las 18:23

  • Clasificación CÓMO para aquellos que buscan más información sobre la clasificación en Python.

    – Jeyekomon

    30 de mayo de 2018 a las 8:48

  • además de operator.attrgetter(‘attribute_name’) también puede usar funtores como clave como object_list.sort(key=my_sorting_functor(‘my_key’)), omitiendo la implementación intencionalmente.

    – vijay shanker

    08/04/2019 a las 20:07

Avatar de usuario de Kenan Banks
Bancos de Kenan

Para ordenar la lista en su lugar:

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

Para devolver una nueva lista, utilice sorted:

new_list = sorted(orig_list, key=lambda x: x.count, reverse=True)

Explicación:

  • key=lambda x: x.count ordena por conteo.
  • reverse=True clasifica en orden descendente.

Más en clasificación por claves.

  • Ningún problema. por cierto, si muhuk tiene razón y es una lista de objetos de Django, debería considerar su solución. Sin embargo, para el caso general de ordenar objetos, mi solución probablemente sea la mejor práctica.

    – Kenan Banks

    31 de diciembre de 2008 a las 17:12

  • En listas grandes, obtendrá un mejor rendimiento utilizando operator.attrgetter(‘count’) como clave. Esta es solo una forma optimizada (nivel inferior) de la función lambda en esta respuesta.

    – David Eyk

    31 de diciembre de 2008 a las 19:35

  • Gracias por la gran respuesta. En caso de que sea una lista de diccionarios y ‘contar’ sea una de sus claves, entonces debe cambiarse como se muestra a continuación: ut.sort(key=lambda x: x[‘count’]inversa=Verdadero)

    – dganesh2002

    8 dic 2016 a las 21:20


  • Supongo que merece la siguiente actualización: si es necesario ordenar por varios campos, podría lograrse mediante llamadas consecutivas a sort(), porque Python está usando un algoritmo de ordenación estable.

    – uuu777

    23 de febrero de 2020 a las 14:41

  • Gracias @KenanBanks, tenías razón. De manera molesta, Outlook estaba haciendo algunas cosas raras con las zonas horarias del calendario, por lo que algunas llegaron sin los detalles de la zona horaria… ¡no tengo idea de por qué!

    – petysmith

    20 de abril de 2022 a las 6:05

avatar de usuario de tzot
tzot

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

  • Aquí usaría el nombre de variable “keyfun” en lugar de “cmpfun” para evitar confusiones. El método sort() también acepta una función de comparación a través del argumento cmp=.

    – akaihola

    2 de enero de 2009 a las 12:16

  • Esto no parece funcionar si el objeto tiene atributos agregados dinámicamente (si lo ha hecho self.__dict__ = {'some':'dict'} después de la __init__ método). Aunque no sé por qué debería ser diferente.

    – tutuca

    07/01/2013 a las 20:40


  • @tutuca: nunca he reemplazado la instancia __dict__. Tenga en cuenta que “un objeto que tiene atributos agregados dinámicamente” y “establecer los atributos de un objeto __dict__ atributo” son conceptos casi ortogonales. Lo digo porque su comentario parece implicar que establecer el __dict__ El atributo es un requisito para agregar atributos dinámicamente.

    – tzot

    9 de enero de 2013 a las 23:14


  • @tzot: si entiendo el uso de operator.attrgetterpodría proporcionar una función con cualquier nombre de propiedad y devolver una colección ordenada.

    – Resumen

    24 de febrero de 2016 a las 18:16

  • Para aquellos que buscan más información: wiki.python.org/moin/HowTo/Sorting#Operator_Module_Functions

    – alxs

    20 de enero de 2017 a las 15:01

Avatar de usuario de Jose M Vidal
José Vidal

Los lectores deben notar que el método key=:

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

es muchas veces más rápido que agregar operadores de comparación enriquecidos a los objetos. Me sorprendió leer esto (página 485 de “Python in a Nutshell”). Puede 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 más de 10 veces más lento, pero el libro dice que es solo unas 5 veces más lento en general. 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 el antiguo .sort(). Espero que arreglen eso.

  • Definición __cmp__ es equivalente a llamar .sort(cmp=lambda)no .sort(key=lambda)por lo que no es extraño en absoluto.

    – tzot

    9 de septiembre de 2019 a las 7:46

  • @tzot tiene toda la razón. El primer tipo tiene que comparar objetos entre sí una y otra vez. La segunda ordenación accede a cada objeto solo una vez para extraer su valor de conteo y luego realiza una ordenación numérica simple que está altamente optimizada. Una comparación más justa sería longList2.sort(cmp = cmp). Probé esto y funcionó casi igual que .sort(). (Además: tenga en cuenta que el parámetro de clasificación “cmp” se eliminó en Python 3).

    –Bryan Roach

    29 de octubre de 2019 a las 4:56

  • cmp quedó en desuso en Python 3: docs.python.org/3/howto/…

    – neves

    2 feb 2021 a las 22:40

Enfoque orientado a objetos

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

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]

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

avatar de usuario de muhuk
muhuk

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

avatar de usuario de rob
robar

Agregue operadores de comparación enriquecidos a la clase de objeto, luego use el método sort() de la lista.
Ver rica comparación en python.


Actualizar: 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.

¿Ha sido útil esta solución?