5
>>> d2
{'egg': 3, 'ham': {'grill': 4, 'fry': 6, 'bake': 5}, 'spam': 2}
>>> d2.get('spamx',99)
99
>>> d2.get('ham')['fry']
6

I want to get value of fry inside of ham, if not, get value, 99 or 88 as the 2nd example. But how?

fortran
  • 67,715
  • 23
  • 125
  • 170
litd
  • 51
  • 1
  • 2

4 Answers4

14
d2.get('ham', {}).get('fry', 88)

I would probably break it down into several statements in real life.

ham = d2.get('ham', {})
fry = ham.get('fry', 88)
Oddthinking
  • 21,437
  • 19
  • 76
  • 115
  • 3
    rather `d2.get('ham', {}).get('fry', 99)`, not ? (DRY!) – mykhal Aug 09 '10 at 06:21
  • 1
    Making something readable != repeating yourself. Repeating yourself would be to write a function to get ham's fry, and then another function to get spam's fry, when both functions could be written as one function which takes ham/spam as a parameter. – Jesse Dhillon Aug 09 '10 at 06:38
  • @Jesse, The previous version had two references to 'fry' and two references to the magic number being returned. The suggestion was a distinct improvement. – Oddthinking Aug 09 '10 at 16:44
  • I'm not seeing that version anywhere. – Jesse Dhillon Aug 09 '10 at 19:12
  • @Jesse, odd. Neither am I. It took me about 3 or 4 edits to get this right, but SO seems to have collapsed it into 1, making me look far more decisive than I am. – Oddthinking Aug 09 '10 at 20:16
  • @Oddthinking, I think if the edits are clustered within a certain window of time, they are collapsed. – Jesse Dhillon Aug 10 '10 at 05:05
  • Note: This breaks if `d2.get('ham'...` returns something that is not a dict (if `d2['ham']` was `'other'` say) – Peter Gibson Oct 20 '14 at 00:57
4

For the default values of get to work correctly the first default needs to be a dictionary, so that you can chain the .get calls correctly if the first fails.

d.get('ham',{}).get('fry',88)

you could also use a try, except block

def get_ham_fry()
  try:
    return d['ham']['fry']
  except AttributeError,e:
    return 88
Michael Anderson
  • 61,385
  • 7
  • 119
  • 164
  • 1
    String indexing will raise either a `KeyError` if no key is found, or a `TypeError` if the item doesn't support string indexing. I think `AttributeError` is only appropriate for the `get` calls. – Peter Gibson Oct 20 '14 at 00:55
4

If you need to do this a lot, you can write a helper function

def get_nested(d, list_of_keys, default):
    for k in list_of_keys:
        if k not in d: 
            return default
        d=d[k]
    return d

print get_nested(d2,['ham','spam'],99)
print get_nested(d2,['ham','grill'],99)
John La Rooy
  • 263,347
  • 47
  • 334
  • 476
1

Here's a solution for dealing with nested dictionaries:

def get(root, *keys):
    """
    Returns root[k_1][k_2]...[k_n] if all k_1, ..., k_n are valid keys/indices. 
    Returns None otherwise
    """
    if not keys:
        return root
    if keys[0] not in root:
        return None
    if keys[0] in root:
        return get(root[keys[0]], *keys[1:])

Usage:

>>> d = {'a': 1, 'b': {'c': 3}}
>>> get(d, 'b', 'c')
3
>>> get(d. 'key that's not in d')
None
>>> get(d)
{'a': 1, 'b': {'c': 3}}
Michael
  • 1,781
  • 6
  • 18
  • 24