web-dev-qa-db-fra.com

Comment faire que heapq évalue le tas d'un attribut spécifique?

Je souhaite garder un tas d'objets, pas seulement des chiffres. Ils auront un attribut entier en eux que le tas peut trier. La méthode la plus simple pour utiliser des tas en python est heapq, mais comment puis-je lui dire de trier par un attribut spécifique lors de l'utilisation de heapq?

28
coffee

heapq trie les objets de la même manière que list.sort; définissez donc simplement une méthode __cmp__() dans votre définition de classe, qui se comparera à une autre instance de la même classe:

def __cmp__(self, other):
    return cmp(self.intAttribute, other.intAttribute)

Fonctionne en Python 2.x.

Dans l'utilisation 3.x:

def __lt__(self, other):
    return self.intAttribute < other.intAttribute
46
eumiro

Selon l'exemple de documentation , vous pouvez utiliser des tuples, et le tri se fera par le premier élément du tuple:

>>> h = []
>>> heappush(h, (5, 'write code'))
>>> heappush(h, (7, 'release product'))
>>> heappush(h, (1, 'write spec'))
>>> heappush(h, (3, 'create tests'))
>>> heappop(h)
(1, 'write spec')

Donc, si vous ne voulez pas (ou ne pouvez pas?) Faire une méthode __cmp__, vous pouvez extraire manuellement votre clé de tri au moment du Push.

Notez que si les premiers éléments d'une paire de n-uplets sont égaux, d'autres éléments seront comparés. Si ce n'est pas ce que vous voulez, vous devez vous assurer que chaque premier élément est unique.

36
Jander

Selon le Document officiel , une solution à ce problème consiste à stocker les entrées sous forme de nuplets (consultez les sections 8.4.1 et 8.4.2 ).

Par exemple, votre objet ressemble à ceci dans le format (Clé, valeur_1, valeur_2) de tuple

Lorsque vous placez les objets (tuples} _) dans tas, le premier attribut de l'objet est comparé (dans ce cas, il s'agit de clé). Si une égalité se produit, les testaments de tas utilisent l'attribut suivant (c'est-à-dire valeur_1), etc.

Par exemple:

import heapq

heap = []
heapq.heappush(heap, (0,'one', 1))
heapq.heappush(heap, (1,'two', 11))
heapq.heappush(heap, (1, 'two', 2))
heapq.heappush(heap, (1, 'one', 3))
heapq.heappush(heap, (1,'two', 3))
heapq.heappush(heap, (1,'one', 4))
heapq.heappush(heap, (1,'two', 5))
heapq.heappush(heap, (1,'one', 1))

show_tree(heap)

Sortie:

                                      (0, 'one', 1)                                       
                (1, 'one', 1)                                (1, 'one', 4)                
    (1, 'one', 3)         (1, 'two', 3)         (1, 'two', 2)         (1, 'two', 5)     
(1, 'two', 11)

A propos de Pretty print un tas en python (mise à jour du lien): show_tree ()

3
Catbuilts

Malheureusement, vous ne pouvez pas, bien que ce soit une fonctionnalité souvent demandée.

Une option serait d'insérer des nuplets (clé, valeur) dans le tas. Toutefois, cela ne fonctionnera pas si les valeurs génèrent une exception lors de la comparaison (elles seront comparées en cas d'égalité entre les clés).

Une deuxième option serait de définir une méthode __lt__ (inférieure à) dans la classe qui utilisera l'attribut approprié pour comparer les éléments à trier. Cependant, cela pourrait ne pas être possible si les objets ont été créés par un autre package ou si vous avez besoin de les comparer différemment ailleurs dans le programme.

Une troisième option serait d'utiliser la classe sortlist class du module blist (disclaimer: je suis l'auteur). Le constructeur de sortedlist prend un paramètre key qui vous permet de spécifier une fonction pour renvoyer la clé de tri d'un élément, similaire au paramètre key de list.sort et sorted.

3
Daniel Stutzbach

Vous pouvez implémenter un tas de pirates. Notez l'utilisation de popitem () pour obtenir l'élément le moins prioritaire. 

import heapdict as hd
import string
import numpy as np

h = hd.heapdict()
keys = [char for char in string.ascii_lowercase[:10]]
vals = [i for i in np.random.randint(0,10, 10)]
for k,v in Zip(keys,vals):
    h[k] = v
for i in range(len(vals)):
    print h.popitem()
0
DanGoodrick