Reputation: 7
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
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
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
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