2009-03-16 7 views
66

Beaucoup de fois en Perl, je vais faire quelque chose comme ceci:Quelle est la meilleure façon d'initialiser un dict de dicts en Python?

$myhash{foo}{bar}{baz} = 1 

Comment pourrais-je traduire ce à Python? Jusqu'à présent j'ai:

if not 'foo' in myhash: 
    myhash['foo'] = {} 
if not 'bar' in myhash['foo']: 
    myhash['foo']['bar'] = {} 
myhash['foo']['bar']['baz'] = 1 

Y a-t-il un meilleur moyen?

+0

hum, en effet l'autre a été demandé 5 jours avant et tous les deux ont 4 ans ... –

Répondre

83
class AutoVivification(dict): 
    """Implementation of perl's autovivification feature.""" 
    def __getitem__(self, item): 
     try: 
      return dict.__getitem__(self, item) 
     except KeyError: 
      value = self[item] = type(self)() 
      return value 

Test:

a = AutoVivification() 

a[1][2][3] = 4 
a[1][3][3] = 5 
a[1][2]['test'] = 6 

print a 

Sortie:

{1: {2: {'test': 6, 3: 4}, 3: {3: 5}}} 
+3

Est-il possible de l'étendre afin qu'il prenne en charge le comportement suivant: a [1] [2] [3] + = some_value. Donc, si la clé n'existait pas à l'avance, alors a [1] [2] [3] serait initialisé avec la valeur par défaut du type (une_valeur)? –

+4

Cette fonction a pour effet secondaire que toute tentative d'obtention d'une clé non existante crée également la clé. En règle générale, vous ne souhaitez créer automatiquement une clé que si vous définissez simultanément une clé ou une sous-clé. –

+0

Y a-t-il aussi un moyen de rendre la variable d'affectation? Donc, étant donné que 'var = [1,2,3]', je pourrais faire comme 'a [var] = 1', ce qui pourrait s'étendre à' a [1] [2] [3] = 1'? – PascalVKooten

2

je suppose que la traduction littérale serait:

mydict = {'foo' : { 'bar' : { 'baz':1}}} 

Appel:

>>> mydict['foo']['bar']['baz'] 

vous donne 1.

Cela semble un peu grossière pour moi, cependant.

(Je ne suis pas de type Perl, bien, donc je devine ce que votre perl ne)

+1

Cela fonctionne seulement à l'heure de l'initialisation, cependant, non? – mike

+0

Je ne suis pas sûr de ce que vous voulez dire. – Dana

+0

@Dana, par opposition à l'ajout de nouvelles valeurs à mydict pendant l'exécution. –

2

dictionnaires qui sont emboîtés comme (souvent) a appelé un des objets poor mans. Oui, il y a une implication et cela pourrait correspondre à la nature orientée objet python.

12

Y at-il une raison pour laquelle il doit être un dict de dicts? S'il n'y a aucune raison impérieuse pour cette structure particulière, vous pouvez simplement indexer le dict avec un tuple:

mydict = {('foo', 'bar', 'baz'):1} # Initializes dict with a key/value pair 
mydict[('foo', 'bar', 'baz')]  # Returns 1 

mydict[('foo', 'unbar')] = 2  # Sets a value for a new key 

Les parenthèses sont nécessaires si vous initialisez la dict avec une clé tuple, mais vous pouvez les omettre lors de la création/se valeurs en utilisant []:

mydict = {}      # Initialized the dict 
mydict['foo', 'bar', 'baz'] = 1 # Sets a value 
mydict['foo', 'bar', 'baz']  # Returns 1 
+0

Pouvez-vous être clair sur quand vous pouvez omettre les parenthèses? Est-ce parce que la virgule est l'opérateur de tuple, et que les parenthèses ne sont nécessaires que si nous avons un groupement ambigu? – Kiv

+0

Ajout d'une clarification, thx. – zweiterlinde

+0

Cela peut en fait être plus rapide que les dictionnaires imbriqués car il y a une recherche au lieu de trois. – ChaimG

86

Si la quantité de vous nicher besoin est fixé, collections.defaultdict est merveilleux.

par exemple. imbrication profonde deux:

myhash = collections.defaultdict(dict) 
myhash[1][2] = 3 
myhash[1][3] = 13 
myhash[2][4] = 9 

Si vous voulez aller un autre niveau d'imbrication, vous aurez besoin de faire quelque chose comme:

myhash = collections.defaultdict(lambda : collections.defaultdict(dict)) 
myhash[1][2][3] = 4 
myhash[1][3][3] = 5 
myhash[1][2]['test'] = 6 

modifier: MizardX indique que nous pouvons obtenir avec un plein généricité fonction simple:

import collections 
def makehash(): 
    return collections.defaultdict(makehash) 

maintenant, nous pouvons faire:

myhash = makehash() 
myhash[1][2] = 4 
myhash[1][3] = 8 
myhash[2][5][8] = 17 
# etc 
+4

ou def makehash(): return collections.defaultdict (makehash); myhash = makehash() –

+0

Je n'ai aucun problème avec les fonctions récursives "traditionnelles", mais il y a quelque chose à ce sujet que je trouve non intuitif. Impair. Quoi qu'il en soit, merci! –

+1

Merci pour ça. Ce lambda: defaultdict() est ce dont j'avais besoin. – wheaties

Questions connexes