web-dev-qa-db-fra.com

Plusieurs clés par valeur

Est-il possible d'affecter plusieurs clés par valeur dans un dictionnaire Python. Une solution possible consiste à attribuer une valeur à chaque clé:

dict = {'k1':'v1', 'k2':'v1', 'k3':'v1', 'k4':'v2'}

mais ce n'est pas efficace en mémoire car mon fichier de données est> 2 Go. Sinon, vous pourriez créer un dictionnaire de clés de dictionnaire:

key_dic = {'k1':'k1', 'k2':'k1', 'k3':'k1', 'k4':'k4'}
dict = {'k1':'v1', 'k4':'v2'}
main_key = key_dict['k2']
value = dict[main_key]

Cela prend également beaucoup de temps et d'efforts car je dois parcourir le dictionnaire/fichier entier deux fois. Existe-t-il une autre solution facile et intégrée Python?

Remarque: mes valeurs de dictionnaire ne sont pas de simples chaînes (comme dans la question 'v1', 'v2') plutôt des objets complexes (contient différents autres dictionnaires/listes etc. et pas possible de les décaper)

Remarque: la question semble similaire à Comment puis-je utiliser à la fois une clé et un index pour la même valeur de dictionnaire? Mais je ne recherche pas un dictionnaire ordonné/indexé et je cherche d'autres solutions efficaces (le cas échéant) autres que les deux mentionnées dans cette question.

35
d.putto

De quel type sont les valeurs?

dict = {'k1':MyClass(1), 'k2':MyClass(1)}

donnera des objets de valeur en double, mais

v1 = MyClass(1)
dict = {'k1':v1, 'k2':v1}

entraîne les deux clés se référant au même objet réel.

Dans la question d'origine, vos valeurs sont des chaînes: même si vous déclarez deux fois la même chaîne, je pense qu'elles seront internées dans le même objet dans ce cas


NB. si vous n'êtes pas sûr d'avoir fini avec des doublons, vous pouvez le découvrir comme suit:

if dict['k1'] is dict['k2']:
    print("good: k1 and k2 refer to the same instance")
else:
    print("bad: k1 and k2 refer to different instances")

(is vérifier grâce à J.F.Sebastian, en remplaçant id())

37
Useless

Découvrez ceci - c'est une implémentation de exactement ce que vous demandez: multi_key_dict (ionary)

https://pypi.python.org/pypi/multi_key_dict (sources sur https://github.com/formiaczek/python_data_structures/tree/master/multi_key_dict )

(sur les plates-formes Unix, il s'agit peut-être d'un package et vous pouvez essayer de l'installer avec quelque chose comme:

Sudo apt-get install python-multi-key-dict

pour Debian, ou un équivalent pour votre distribution)

Vous pouvez utiliser différents types de clés mais également des clés du même type. Vous pouvez également parcourir les éléments en utilisant les types de clés de votre choix, par exemple:

m = multi_key_dict()
m['aa', 12] = 12
m['bb', 1] = 'cc and 1'
m['cc', 13] = 'something else'

print m['aa']   # will print '12'
print m[12]     # will also print '12'

# but also:
for key, value in m.iteritems(int):
    print key, ':', value
# will print:1
# 1 : cc and 1
# 12 : 12
# 13 : something else

# and iterating by string keys:
for key, value in m.iteritems(str):
    print key, ':', value
# will print:
# aa : 12
# cc : something else
# bb : cc and 1

m[12] = 20 # now update the value
print m[12]   # will print '20' (updated value)
print m['aa']   # will also print '20' (it maps to the same element)

Il n'y a pas de limite au nombre de clés, donc du code comme:

m['a', 3, 5, 'bb', 33] = 'something' 

est valide et l'une ou l'autre des clés peut être utilisée pour faire référence à la valeur ainsi créée (soit pour la lire/l'écrire, soit pour la supprimer).

Edit: à partir de la version 2.0, il devrait également fonctionner avec python3.

10
formiaczek

Je suis surpris que personne n'ait mentionné l'utilisation de Tuples avec des dictionnaires. Cela fonctionne très bien:

my_dictionary = {}
my_dictionary[('k1', 'k2', 'k3')] = 'v1'
my_dictionary[('k4')] = 'v2'
2
Justin Bozonier

En utilisant python 2,7/3, vous pouvez combiner une paire de valeur Tuple avec la compréhension du dictionnaire.

keys_values = ( (('k1','k2'), 0), (('k3','k4','k5'), 1) )

d = { key : value for keys, value in keys_values for key in keys }

Vous pouvez également mettre à jour le dictionnaire de la même manière.

keys_values = ( (('k1',), int), (('k3','k4','k6'), int) )

d.update({ key : value for keys, value in keys_values for key in keys })

Je ne pense pas que cela va vraiment au cœur de votre question, mais à la lumière du titre, je pense que cela appartient ici.

2
blahreport

La façon la plus simple de le faire est de construire votre dictionnaire à l'aide de la méthode dict.fromkeys (). Il prend une séquence de clés et une valeur comme entrées, puis attribue la valeur à chaque clé.
Votre code serait:

dict = dict.fromkeys(['k1', 'k2', 'k3'], 'v1')
dict.update(dict.fromkeys(['k4'], 'v2'))

Et la sortie est:

print(dict)
{'k1': 'v1', 'k2': 'v1', 'k3': 'v1', 'k4': 'v2'}
2
bloodrootfc

Vous pouvez créer un dictionnaire auxiliaire d'objets déjà créés à partir des données analysées. La clé serait les données analysées, la valeur serait votre objet construit - disons que la valeur de la chaîne devrait être convertie en un objet spécifique. De cette façon, vous pouvez contrôler quand construire le nouvel objet:

existing = {}   # auxiliary dictionary for making the duplicates shared
result = {}
for k, v in parsed_data_generator():
    obj = existing.setdefault(v, MyClass(v))  # could be made more efficient
    result[k] = obj

Tous les objets de valeur en double du dictionnaire result seront alors représentés par un seul objet de la classe MyClass. Une fois le résultat généré, le dictionnaire auxiliaire existing peut être supprimé.

Ici, la dict.setdefault() peut être élégante et brève. Mais vous devriez tester plus tard si la solution la plus bavarde n'est pas plus efficace - voir ci-dessous. La raison en est que MyClass(v) est toujours créé (dans l'exemple ci-dessus) puis jeté si son doublon existe:

existing = {}   # auxiliary dictionary for making the duplicates shared
result = {}
for k, v in parsed_data_generator():
    if v in existing:
        obj = existing[v]
    else:
        obj = MyClass(v)
        existing[v] = obj

    result[k] = obj

Cette technique peut également être utilisée lorsque v n'est pas converti en quelque chose de spécial. Par exemple, si v est une chaîne, la clé et la valeur dans le dictionnaire auxiliaire auront la même valeur. Cependant, l'existence du dictionnaire garantit que l'objet sera partagé (ce qui n'est pas toujours assuré par Python).

1
pepr

J'ai pu obtenir des fonctionnalités similaires en utilisant pandas MultiIndex , bien que dans mon cas les valeurs soient des scalaires:

>>> import numpy
>>> import pandas
>>> keys = [numpy.array(['a', 'b', 'c']), numpy.array([1, 2, 3])]
>>> df = pandas.DataFrame(['val1', 'val2', 'val3'], index=keys)
>>> df.index.names = ['str', 'int']
>>> df.xs('b', axis=0, level='str')
        0
int      
2    val2

>>> df.xs(3, axis=0, level='int')
        0
str      
c    val3
1
robodasha