web-dev-qa-db-fra.com

Recherche d'une clé dans un dictionnaire Python:

Assez nouveau pour Python, toujours aux prises avec tant d’informations. 

Toute la documentation que j'ai vue sur les dictionnaires explique différentes manières d'obtenir une valeur via une clé - mais je recherche une méthode pythonique pour faire le contraire - obtenir une clé via une valeur.

Je sais que je peux parcourir les clés et inspecter leurs valeurs jusqu'à ce que je trouve la valeur que je recherche, puis récupérer la clé, mais je recherche un itinéraire direct.

12
Vector

Il n'y a pas de voie directe. C'est assez facile avec les compréhensions de liste, cependant;

[k for k, v in d.iteritems() if v == desired_value]

Si vous avez besoin de le faire de temps en temps et que vous ne pensez pas que cela vaut la peine de l'indexer également, vous pouvez faire quelque chose comme:

class bidict(dict):
    def key_with_value(self, value, default=None):
        for k, v in self.iteritems():
            if v == value:
                return v
        return default

    def keys_with_value(self, value, default=None):
        return [v for k, v in self.iteritems() if v == value]

Alors d.key_with_value se comporterait plutôt comme d.get, sauf le contraire.

Vous pouvez également créer une classe qui l’indexera automatiquement dans les deux sens. La clé et la valeur doivent être toutes les deux possibles. Voici trois manières de le mettre en œuvre:

  • Dans deux dict distincts, avec l'exposition de certaines méthodes ressemblant à un dict; vous pourriez peut-être faire foo.by_key[key] ou foo.by_value[value]. (Pas de code donné car c'est plus compliqué et je suis paresseux et je pense que c'est sous-optimal de toute façon.)

  • Dans une structure différente, afin que vous puissiez faire d[key] et d.inverse[value]:

    class bidict(dict):
        def __init__(self, *args, **kwargs):
            self.inverse = {}
            super(bidict, self).__init__(key, value)
    
        def __setitem__(self, key, value):
            super(bidict, self).__setitem__(key, value)
            self.inverse[value] = key
    
        def __delitem__(self, key):
            del self.inverse[self[key]]
            super(bidict, self).__delitem__(key)
    
  • Dans la même structure, afin que vous puissiez faire d[key] et d[value]:

    class bidict(dict):
        def __setitem__(self, key, value):
            super(bidict, self).__setitem__(key, value)
            super(bidict, self).__setitem__(value, key)
    
        def __delitem__(self, key):
            super(bidict, self).__delitem__(self[key])
            super(bidict, self).__delitem__(key)
    

(Notamment absentes de ces implémentations de bidictest la méthode updatequi sera légèrement plus complexe (mais help(dict.update) indiquera ce que vous devez couvrir). Sans updatename__, bidict({1:2}) ne ferait pas ce que vous vouliez couvrir, ni d.update({1:2}) .)

Déterminez également si une autre structure de données serait plus appropriée.

27
Chris Morgan

Étant donné que votre dictionnaire peut contenir des valeurs en double (c'est-à-dire {'a': 'A', 'b': 'A'}), le seul moyen de rechercher une clé à partir d'une valeur consiste à effectuer une itération sur le dictionnaire tel que vous le décrivez.

Ou ... construisez le dictionnaire opposé. vous devez le recréer après chaque modification du dictionnaire d'origine.

Ou ... écrivez une classe qui maintient un dictionnaire à double sens. Vous devrez gérer les situations où la valeur en double apparaît.

6
eumiro

la première solution avec la compréhension de la liste est bonne. mais un petit correctif pour python 3.x, au lieu de .iteritems(), il devrait être juste .items():

[k for k, v in d.items() if v == desired_value]
1
Idan Richman

Construire un dictionnaire opposé n’est pas du tout une bonne manière car une ou plusieurs clés ont la même valeur, mais si vous l’inversez, vous devez insérer la structure clé: [valeur1, ...] qui vous mènera à un autre problème.

0
Raj Damani