Sharky Le Shark
Sharky Le Shark

Reputation: 7

Is it possible to have a dictionary of dictionary of dictionary in python?

I would like a three-dimensional hash in python - a dictionary of a dictionary of a dictionary. I don't know if this is possible and I'm having some problems I don't understand that makes me suspect it isn't possible. An example piece of code is below that reads two comma separated files ('tv.txt' and 'film.txt') and puts the data on a dictionary of dictionary of dictionary (dOfDofD). The two files look like this:

tv.txt

Muppets,female,Miss Piggy
Muppets,male,Kermit
Simpsons,female,Marge 
Simpsons,male,Homer

film.txt

Gone with the Wind,female,Vivien Leigh
Gone with the Wind,male,Clark Gable 
Anthony and Cleopatra,female,Elizabeth Taylor 
Anthony and Cleopatra,male,Richard Burton

My code reads these two files and creates the dOfDofD, but when it comes to retrieve the data, it seems some of it is missing in a systematic way: only the last example of the final level of dictionary appears to exist. The code is:

#!/usr/bin/env python
import string

dOfDofD = {}

mediaList = ['tv', 'film']

showSet = set()

for media in mediaList:
    fName = media + ".txt"
    f = open(fName, 'r')
    for line in f:
        line = line.rstrip('\n')
        dataList = string.splitfields(line, ',')
        show = dataList[0]
        showSet.add(show)
        gender = dataList[1]
        name = dataList[2]
        dOfDofD[show] = {}
        dOfDofD[show][media] = {}
        dOfDofD[show][media][gender] = name
    f.close()

for show in showSet:
    for media in dOfDofD[show]:
        for gender in dOfDofD[show][media]:
            print "show: %s. media: %s. gender: %s. character: %s." % \
                    (show, media, gender, dOfDofD[show][media][gender])

This only prints out the male characters, as if the female ones have been overwritten (which were set up first).

show: Simpsons. media: tv. gender: male. character: Homer. 
show: Gone with the Wind. media: film. gender: male. character: Clark Gable.
show: Muppets. media: tv. gender: male. character: Kermit. 
show: Anthony and Cleopatra. media: film. gender: male. character: Richard Burton.

I'm running python 2.7.6 on Ubuntu 14.04.

Any ideas what I'm doing wrong?

Upvotes: 0

Views: 88

Answers (3)

pzp
pzp

Reputation: 6617

The problem is that each time you are adding a new pair to the dictionary, you're creating new dicts for dOfDofD[show] and dOfDofD[show][media]. To fix this, you could use dict.setdefault(). Here's the line: dOfDOfD.setdefault(show, {}).setdefault(media, {})[gender] = name.

dOfDOfD = {}

mediaList = ['tv', 'film']

showSet = set()

for media in mediaList:
    with open('{}.txt'.format(media), 'r') as f:
        for line in f:
            show, gender, name = line.split(',')
            showSet.add(show)
            dOfDOfD.setdefault(show, {}).setdefault(media, {})[gender] = name

for show in showSet:
    for media in dOfDOfD[show]:
        for gender in dOfDOfD[show][media]:
            print "show: {}. media: {}. gender: {}. character: {}.".format(
                show, media, gender, dOfDOfD[show][media][gender])

show: Simpsons. media: tv. gender: male. character: Homer.

show: Simpsons. media: tv. gender: female. character: Marge.

show: Gone with the Wind. media: film. gender: male. character: Clark Gable.

show: Gone with the Wind. media: film. gender: female. character: Vivien Leigh.

show: Muppets. media: tv. gender: male. character: Kermit.

show: Muppets. media: tv. gender: female. character: Miss Piggy.

show: Anthony and Cleopatra. media: film. gender: male. character: Richard Burton.

show: Anthony and Cleopatra. media: film. gender: female. character: Elizabeth Taylor.

In addition, I changed some other things in your program. I renamed dOfDofD to dOfDOfD (proper camel casing). I used a with...as statement to open the files, which provides numerous advantages (I will not go into them here, but if you're curious just Google it). And I changed your string formatting with `%s' to use the str.format() method (the now recommended way of formatting a string).

Upvotes: 0

Sharky Le Shark
Sharky Le Shark

Reputation: 7

Following from hiro protagonist's post, here's the snipet of code I used:

for media in mediaList:
    fName = media + ".txt"
    f = open(fName, 'r')
    for line in f:
        line = line.rstrip('\n')
        dataList = string.splitfields(line, ',')
        show = dataList[0]
        showSet.add(show)
        gender = dataList[1]
        name = dataList[2]
        if show in dOfDofD:
            if media in dOfDofD[show]:
                dOfDofD[show][media][gender] = name
            else:
                dOfDofD[show][media] = {}
                dOfDofD[show][media][gender] = name
        else:
            dOfDofD[show] = {}
            dOfDofD[show][media] = {}
            dOfDofD[show][media][gender] = name
    f.close()

Upvotes: 0

hiro protagonist
hiro protagonist

Reputation: 46921

yes: creating nested dictionaries is possible.

you keep overwriting dOfDofD[show] for every line in your text file.

you could try this:

if show in dOfDofD:
    show_dict = dOfDofD[show]
else:
    show_dict = {}
    dOfDofD[show] = show_dict
# do something with show_dict

and something similar for dOfDofD[show][media].

or you could use defaultdict.

and seeing that your input file is comma-separated: have a look at the python csv module.

Upvotes: 1

Related Questions