37 votes

Plusieurs clés par valeur

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

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

mais cela n'est pas efficace en termes de mémoire car mon fichier de données fait > 2 Go. Autrement, 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']
valeur = dict[main_key]

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

Note: mes valeurs de dictionnaire ne sont pas des chaînes simples (comme dans la question 'v1', 'v2') mais plutôt des objets complexes (contenant différents autres dictionnaires/listes, etc. et il n'est pas possible de les pickler)

Note: 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 cherche pas un dictionnaire ordonné/indexé et je recherche d'autres solutions efficaces (si elles existent) autres que les deux mentionnées dans cette question.

3 votes

Vous êtes peu susceptible de trouver une solution meilleure que votre première. Il suffit de mettre plusieurs clés dans le dictionnaire. Pourquoi cela ne suffit-il pas?

0 votes

Comme je l'ai dit dans la question, le fichier est très volumineux, donc mettre les mêmes valeurs plusieurs fois nécessite beaucoup de mémoire.

10 votes

Les valeurs en python ne sont pas nécessairement dupliquées en python, même en utilisant plusieurs clés pour y faire référence.

38voto

Useless Points 18909

De quels types sont les valeurs ?

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

donnera des objets de valeurs en double, mais

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

résulte en ce que les deux clés font référence au même objet réel.

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


NB. si vous n'êtes pas sûr d'avoir obtenu des doublons, vous pouvez le vérifier comme suit :

if dict['k1'] is dict['k2']:
    print("bon : k1 et k2 réfèrent au même instance")
else:
    print("mauvais : k1 et k2 réfèrent à des instances différentes")

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

0 votes

Les valeurs du dictionnaire ne sont pas de simples chaînes de caractères mais plutôt des objets complexes (contenant différents autres dictionnaires/listes, etc.)

4 votes

Vous pourriez utiliser obj1 est obj2 pour tester l'identité des objets au lieu de id(obj1) == id(obj2)

1 votes

@d.putto - dans ce cas, assurez-vous de stocker la même instance avec chaque clé, et non pas une instance en double avec le même contenu (comme dans mes deux premiers exemples). Si vous n'êtes pas sûr, confirmez avec is

12voto

formiaczek Points 89

Consultez ceci - c'est une implémentation exacte de ce que vous demandez : multi_key_dict(ionary)

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

(sur les plateformes Unix, il est possible qu'il soit fourni en tant que 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 utilisez différents types de clés mais aussi des clés du même type. Vous pouvez également itérer sur 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 et 1'
m['cc', 13] = 'quelque chose d'autre'

print m['aa']   # affichera '12'
print m[12]     # affichera également '12'

# mais aussi :
for key, value in m.iteritems(int):
    print key, ':', value
# affichera :
# 1 : cc et 1
# 12 : 12
# 13 : quelque chose d'autre

# et en itérant sur des clés de type string :
for key, value in m.iteritems(str):
    print key, ':', value
# affichera:
# aa : 12
# cc : quelque chose d'autre
# bb : cc et 1

m[12] = 20 # maintenant mettre à jour la valeur
print m[12]   # affichera '20' (valeur mise à jour)
print m['aa']   # affichera également '20' (il se réfère au même élément)

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

m['a', 3, 5, 'bb', 33] = 'quelque chose' 

est valide, et l'un des clés peut être utilisée pour se référer à la valeur ainsi créée (pour la lire / écrire ou la supprimer).

Édition : À partir de la version 2.0, cela devrait également fonctionner avec python3.

0 votes

Qu'en est-il du cas où deux clés contiennent le même élément, disons m['aa', 12] et m[12, 'bb']. Que se passe-t-il lorsque vous faites m[12] = 20

0 votes

Si deux clés contiennent le même élément (ce qu'on essaie en réalité de réaliser), cet élément peut être accédé avec l'une ou l'autre de ces clés, et la suppression de l'une d'entre elles (par exemple del[12] ou del['aa']) supprimera l'élément et il ne pourra plus être accédé par aucune autre clé (les références à cette autre clé(s) sont également supprimées).

0 votes

m[12] = 20 fera soit : 1) si m ne contient pas d'élément avec cette clé (unique ou multiple) - il créera un élément avec la clé 12 et l'assiéra (map) à la valeur 20 (comme un dictionnaire standard). 2) Si l'élément existe (comme dans l'exemple ci-dessus) il mettra à jour la valeur référencée par cette clé. Si cette valeur était associée à plusieurs clés - y accéder avec ces autres clés fera référence à cette valeur (mise à jour). J'ai mis à jour l'exemple ci-dessus pour refléter cela.

2voto

blahreport Points 890

En utilisant python 2.7/3, vous pouvez combiner une paire de tuple, valeur avec une compréhension de 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 manière similaire.

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 réponde vraiment à votre question, mais compte tenu du titre, je pense que cela a sa place ici.

0 votes

J'ai trouvé votre message dans une critique. Je ne comprends pas suffisamment ce sujet pour juger votre réponse, donc je saute cette critique, laissant aux autres le soin de le faire. Quoi qu'il en soit, laissez-moi dire qu'une réponse qui dit "Je ne pense pas que cela corresponde vraiment à la question de fond" ne me semble pas bonne. Le fait que vous abordiez le titre ne suffit pas. Encore une fois, comme je l'ai dit, je ne comprends pas suffisamment ce sujet, donc je ne vais pas voter de quelque manière que ce soit. Mais je suggère quand même d'améliorer cette réponse pour éviter d'écrire "Je ne pense pas que cela corresponde vraiment à la question de fond".

0 votes

Désolé pour la confusion Fabio, bien que je pense que nous pourrions avoir un désaccord sémantique sur ce que signifie être au cœur d'une question. Je pense que ma solution est adaptée pour attribuer plusieurs clés à une valeur commune et c'est effectivement la question telle qu'elle est posée dans le titre et la première partie du post de d.putto. Le deuxième critère, être "économe en mémoire", est ce que j'ai considéré comme étant au cœur de la question. J'espère que cela aidera à valider ma réponse. Néanmoins, j'ai l'impression que votre commentaire n'aborde ni le message d'origine ni l'essence de ma réponse et est donc hors sujet par rapport à la discussion.

2voto

bloodrootfc Points 314

La manière la plus simple de le faire est de construire votre dictionnaire en utilisant la méthode dict.fromkeys(). Elle prend une séquence de clés et une valeur en entrée, 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'}

1voto

robodasha Points 236

J'ai pu obtenir une fonctionnalité similaire en utilisant pandas MultiIndex, bien que dans mon cas les valeurs soient 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

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