tutorial - self en python




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

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.


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]

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




count