1

I'm trying to parse/loop through a deeply nested dictionary/tuple such as this:

#!/usr/bin/env python

movies = {
    'in_bruges': {
        'display': 'In Bruges',
        'actors': {
            'Colin Farrel': {
                'age': '99',
                'height': '160'
            },
            'Brendan Gleeson': {
                'age': '88',
                'height': '158'
            }
            # many more...
        }
    },
    'fargo': {
        'display': 'Fargo',
        'actors': {
            'William H. Macy': {
                'age': '109',
                'height': '120'
            },
            'Steve Buscemi': {
                'age': '11',
                'height': '118'
            }
            # many more...
        }
    }
    # many more...
}

I can access individual fields just fine:

print(movies['in_bruges']['actors']['Brendan Gleeson']['age']);
88

But I'm having a hard time looping through the whole thing. This is what I have so far...

for movie, val in movies.iteritems():
    print movie
    for v in val.values():
        print v
        for x in v.values():  # <-- BREAKS HERE
            print x

Error:

AttributeError: 'str' object has no attribute 'values'

I understand why it's breaking... just not sure how to fix it. Also, and probably more importantly, is there a better way to do this? This is a mix of a dictionary and a tuple (correct me if I'm wrong). Should I structure it differently? Or use a different method altogether? I'd rather not read it in from a csv or text...

3 Answers 3

2

Just add the check of type before access to values/value:

isinstance(v, dict)

or

isinstance(v, basestring)

And the code will be:

for movie, val in movies.iteritems():
    print movie
    for v in val.values():
        if isinstance(v, dict):
            for x in v.values():
                print x
        elif isinstance(v, basestring):
            print v
Sign up to request clarification or add additional context in comments.

Comments

1

Not all your values are dictionaries; some are strings:

# ...
'in_bruges': {
    'display': 'In Bruges',
    'actors': {
        # ...

That's a string and a dictionary. You'll have to vary your handling:

for movie, val in movies.iteritems():
    print movie
    for v in val.values():
        if hasattr(v, 'values'):
            for x in v.values():
                print x
        else:
            print v

Here I used ducktyping; checking for a method I want to use, rather than a type; if it is present we assume it can be treated as a dictionary. You could also use isinstance(v, dict) but the above is more flexible as it allows anything with the right method to be treated the same.

You could use recursion to handle arbitrary values at any depth:

def print_dictionary(d, indent=0):
    try:
        for k, v in d.iteritems():
            print (indent * '  '), k, '->'
            print_dictionary(v, indent + 1)
    except AttributeError:
        print (indent * '  '), d

Here I used exception handling; if the dict.iteritems() method isn't present an AttributeError is raised. This technique is called Look Before You Leap.

With your demo dictionary this outputs:

>>> print_dictionary(movies)
 fargo ->
   actors ->
     William H. Macy ->
       age ->
         109
       height ->
         120
     Steve Buscemi ->
       age ->
         11
       height ->
         118
   display ->
     Fargo
 in_bruges ->
   actors ->
     Brendan Gleeson ->
       age ->
         88
       height ->
         158
     Colin Farrel ->
       age ->
         99
       height ->
         160
   display ->
     In Bruges

1 Comment

Wow this is excellent! Works perfectly. Thanks so much for the thoughtful explanation, Martijn. Really appreciate it.
1
# for all movies
for movie, movie_details in movies.iteritems(): 
    print movie
    display = movie_details['display']
    print display

    # for all actors in this movie
    for actor,actor_details in movie_details['actors'].iteritems():
        print actor
        print actor_details['age']
        print actor_details['height']

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.