4 votes

Utilisation d'objets Python non hachables comme clés dans les dictionnaires

Python ne permet pas aux objets non hachables d'être utilisés comme clés dans d'autres dictionnaires. Comme l'a souligné Andrey Vlasovskikh, il existe une solution de contournement intéressante pour le cas particulier de l'utilisation de dictionnaires non imbriqués comme clés :

frozenset(a.items())#Can be put in the dictionary instead

Existe-t-il une méthode permettant d'utiliser des objets arbitraires comme clés dans les dictionnaires ?

Ejemplo :

Comment cela pourrait-il être utilisé comme une clé ?

{"a":1, "b":{"c":10}}

Il est extrêmement rare que vous ayez à utiliser quelque chose comme cela dans votre code. Si vous pensez que c'est le cas, envisagez d'abord de modifier votre modèle de données.

Cas d'utilisation exact

Le cas d'utilisation est la mise en cache des appels à une fonction arbitraire de mot-clé seulement. Chaque clé du dictionnaire est une chaîne de caractères (le nom de l'argument) et les objets peuvent être assez compliqués, consistant en des dictionnaires, listes, tuples, etc. superposés.

Problèmes connexes

Ce sous-problème a été séparé de celui de la le problème ici . Les solutions proposées ici traitent du cas où les dictionnaires ne sont pas superposés.

2voto

koo Points 1825

Je suis d'accord avec Lennart Regebro pour dire que vous ne le faites pas. Cependant, je trouve souvent utile de mettre en cache certains appels de fonctions, objets appelables et/ou objets Flyweight, car ils peuvent utiliser des arguments sous forme de mots-clés.

Mais si vous le voulez vraiment, essayez pickle.dumps (ou cPickle si python 2.6) comme un hack rapide et sale. Il est beaucoup plus rapide que toutes les réponses qui utilisent des appels récursifs pour rendre les éléments immuables, et les chaînes de caractères sont hachables.

import pickle
hashable_str = pickle.dumps(unhashable_object)

1voto

Chris Lutz Points 34157

Avec récursion ¡!

def make_hashable(h):
    items = h.items()
    for item in items:
        if type(items) == dict:
            item = make_hashable(item)
    return frozenset(items)

Vous pouvez ajouter d'autres tests de type pour tout autre type mutable que vous souhaitez rendre hachable. Cela ne devrait pas être difficile.

1voto

wberry Points 6068

J'ai rencontré ce problème en utilisant un décorateur qui met en cache les résultats des appels précédents en fonction de la signature de l'appel. Je ne suis pas d'accord avec les commentaires/réponses qui disent "vous ne devriez pas faire ça", mais je pense qu'il est important de reconnaître le potentiel d'un comportement surprenant et inattendu lorsqu'on s'engage dans cette voie. Je pense que puisque les instances sont à la fois mutables et hachables, et qu'il ne semble pas pratique de changer cela, il n'y a rien d'intrinsèquement mauvais à créer des équivalents hachables de types ou d'objets non hachables. Mais bien sûr, ce n'est que mon opinion.

Pour tous ceux qui ont besoin de la compatibilité avec Python 2.5, le texte ci-dessous peut être utile. Je l'ai basé sur la réponse précédente.

from itertools import imap
tuplemap = lambda f, data: tuple(imap(f, data))
def make_hashable(obj):
  u"Returns a deep, non-destructive conversion of given object to an equivalent hashable object"
  if isinstance(obj, list):
    return tuplemap(make_hashable, iter(obj))
  elif isinstance(obj, dict):
    return frozenset(tuplemap(make_hashable, obj.iteritems()))
  elif hasattr(obj, '__hash__') and callable(obj.__hash__):
    try:
      obj.__hash__()
    except:
      if hasattr(obj, '__iter__') and callable(obj.__iter__):
        return tuplemap(make_hashable, iter(obj))
      else:
        raise NotImplementedError, 'object of type %s cannot be made hashable' % (type(obj),)
    else:
      return obj
  elif hasattr(obj, '__iter__') and callable(obj.__iter__):
    return tuplemap(make_hashable, iter(obj))
  else:
    raise NotImplementedError, 'object of type %s cannot be made hashable' % (type, obj)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X