I realize this is an old question, but I was just looking for a simple, clean way to walk nested dicts and this is the closest thing my limited searching has come up with. oadams' answer isn't useful enough if you want more than just filenames and spicavigo's answer looks complicated.
I ended up just rolling my own that acts similar to how os.walk treats directorys, except that it returns all key/value information.
It returns an iterator and for each directory in the "tree" of nested dicts, the iterator returns (path, sub-dicts, values) where:
- path is the path to the dict
- sub-dicts is a tuple of (key,dict) pairs for each sub-dict in this dict
- values is a tuple of (key,value) pairs for each (non-dict) item in this dict
def walk(d):
'''
Walk a tree (nested dicts).
For each 'path', or dict, in the tree, returns a 3-tuple containing:
(path, sub-dicts, values)
where:
* path is the path to the dict
* sub-dicts is a tuple of (key,dict) pairs for each sub-dict in this dict
* values is a tuple of (key,value) pairs for each (non-dict) item in this dict
'''
# nested dict keys
nested_keys = tuple(k for k in d.keys() if isinstance(d[k],dict))
# key/value pairs for non-dicts
items = tuple((k,d[k]) for k in d.keys() if k not in nested_keys)
# return path, key/sub-dict pairs, and key/value pairs
yield ('/', [(k,d[k]) for k in nested_keys], items)
# recurse each subdict
for k in nested_keys:
for res in walk(d[k]):
# for each result, stick key in path and pass on
res = ('/%s' % k + res[0], res[1], res[2])
yield res
Here is the code I used to test it, though it has a a couple other unrelated (but neat) stuff in it:
import simplejson as json
from collections import defaultdict
# see https://gist.github.com/2012250
tree = lambda: defaultdict(tree)
def walk(d):
'''
Walk a tree (nested dicts).
For each 'path', or dict, in the tree, returns a 3-tuple containing:
(path, sub-dicts, values)
where:
* path is the path to the dict
* sub-dicts is a tuple of (key,dict) pairs for each sub-dict in this dict
* values is a tuple of (key,value) pairs for each (non-dict) item in this dict
'''
# nested dict keys
nested_keys = tuple(k for k in d.keys() if isinstance(d[k],dict))
# key/value pairs for non-dicts
items = tuple((k,d[k]) for k in d.keys() if k not in nested_keys)
# return path, key/sub-dict pairs, and key/value pairs
yield ('/', [(k,d[k]) for k in nested_keys], items)
# recurse each subdict
for k in nested_keys:
for res in walk(d[k]):
# for each result, stick key in path and pass on
res = ('/%s' % k + res[0], res[1], res[2])
yield res
# use fancy tree to store arbitrary nested paths/values
mem = tree()
root = mem['SomeRootDirectory']
root['foo.txt'] = None
root['bar.txt'] = None
root['Stories']['Horror']['scary.txt'] = None
root['Stories']['Horror']['Trash']['notscary.txt'] = None
root['Stories']['Cyberpunk']
root['Poems']['doyoureadme.txt'] = None
# convert to json string
s = json.dumps(mem, indent=2)
#print mem
print s
print
# json.loads converts to nested dicts, need to walk them
for (path, dicts, items) in walk(json.loads(s)):
# this will print every path
print '[%s]' % path
for key,val in items:
# this will print every key,value pair (skips empty paths)
print '%s = %s' % (path+key,val)
print
The output looks like:
{
"SomeRootDirectory": {
"foo.txt": null,
"Stories": {
"Horror": {
"scary.txt": null,
"Trash": {
"notscary.txt": null
}
},
"Cyberpunk": {}
},
"Poems": {
"doyoureadme.txt": null
},
"bar.txt": null
}
}
[/]
[/SomeRootDirectory/]
/SomeRootDirectory/foo.txt = None
/SomeRootDirectory/bar.txt = None
[/SomeRootDirectory/Stories/]
[/SomeRootDirectory/Stories/Horror/]
/SomeRootDirectory/Stories/Horror/scary.txt = None
[/SomeRootDirectory/Stories/Horror/Trash/]
/SomeRootDirectory/Stories/Horror/Trash/notscary.txt = None
[/SomeRootDirectory/Stories/Cyberpunk/]
[/SomeRootDirectory/Poems/]
/SomeRootDirectory/Poems/doyoureadme.txt = None