web-dev-qa-db-fra.com

Pourquoi ne Python définit les ensembles hachables?

Je suis tombé sur un article de blog détaillant comment implémenter une fonction de jeu de puissance en Python. J'ai donc essayé ma propre façon de le faire et j'ai découvert que Python ne peut apparemment pas avoir un ensemble d'ensembles, car l'ensemble n'est pas hachable. C'est gênant, car la définition d'un ensemble de pouvoirs est que c'est un ensemble d'ensembles, et je voulais l'implémenter en utilisant des opérations d'ensemble réelles.

>>> set([ set() ])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'set'

Y a-t-il une bonne raison Python ne sont pas lavables?

58
Dan Burton

Généralement, seuls les objets immuables peuvent être hachés en Python. La variante immuable de set() - frozenset() - est lavable.

103
Sven Marnach

Parce qu'ils sont mutables.

S'ils étaient lavables, un hachage pourrait devenir silencieusement "invalide", ce qui rendrait le hachage inutile.

27
phihag

Depuis les documents Python:

lavable
Un objet peut être haché s'il a une valeur de hachage qui ne change jamais au cours de sa vie (il a besoin d'une méthode de hachage ()), et peut être comparé à d'autres objets (il a besoin d'un eq () ou cmp () méthode). Les objets hachables qui se comparent égaux doivent avoir la même valeur de hachage.

La capacité de hachage rend un objet utilisable comme clé de dictionnaire et membre d'ensemble, car ces structures de données utilisent la valeur de hachage en interne.

Tous les objets intégrés immuables de Python sont lavables, alors qu'aucun conteneur mutable (comme les listes ou les dictionnaires) ne l'est. Les objets qui sont des instances de classes définies par l'utilisateur sont hachables par défaut; ils se comparent tous inégaux, et leur valeur de hachage est leur id ().

16
John Gaines Jr.

Au cas où cela aiderait ... si vous avez vraiment besoin de convertir des éléments non lavables en équivalents lavables pour une raison quelconque, vous pourriez faire quelque chose comme ceci:

from collections import Hashable, MutableSet, MutableSequence, MutableMapping

def make_hashdict(value):
    """
    Inspired by https://stackoverflow.com/questions/1151658/python-hashable-dicts
     - with the added bonus that it inherits from the dict type of value
       so OrderedDict's maintain their order and other subclasses of dict() maintain their attributes
    """
    map_type = type(value)

    class HashableDict(map_type):
        def __init__(self, *args, **kwargs):
            super(HashableDict, self).__init__(*args, **kwargs)
        def __hash__(self):
            return hash(Tuple(sorted(self.items())))

    hashDict = HashableDict(value)

    return hashDict


def make_hashable(value):
    if not isinstance(value, Hashable):
        if isinstance(value, MutableSet):
            value = frozenset(value)
        Elif isinstance(value, MutableSequence):
            value = Tuple(value)
        Elif isinstance(value, MutableMapping):
            value = make_hashdict(value)

        return value

my_set = set()
my_set.add(make_hashable(['a', 'list']))
my_set.add(make_hashable({'a': 1, 'dict': 2}))
my_set.add(make_hashable({'a', 'new', 'set'}))

print my_set

Mon implémentation HashableDict est l'exemple le plus simple et le moins rigoureux de ici . Si vous avez besoin d'un HashableDict plus avancé qui prend en charge le décapage et d'autres choses, vérifiez les nombreuses autres implémentations. Dans ma version ci-dessus, je voulais conserver la classe de dict d'origine, préservant ainsi l'ordre des OrderedDicts. J'utilise également AttrDict de ici pour un accès de type attribut.

Mon exemple ci-dessus ne fait en aucun cas autorité, juste ma solution à un problème similaire où j'avais besoin de stocker certaines choses dans un ensemble et de les "hacher" en premier.

6
flutefreak7