You could use get twice:
example_dict.get('key1', {}).get('key2')
This will return None if either key1 or key2 does not exist.
Note that this could still raise an AttributeError if example_dict['key1'] exists but is not a dict (or a dict-like object with a get method). The try..except code you posted would raise a TypeError instead if example_dict['key1'] is unsubscriptable.
Another difference is that the try...except short-circuits immediately after the first missing key. The chain of get calls does not.
If you wish to preserve the syntax, example_dict['key1']['key2'] but do not want it to ever raise KeyErrors, then you could use the Hasher recipe:
class Hasher(dict):
# https://stackoverflow.com/a/3405143/190597
def __missing__(self, key):
value = self[key] = type(self)()
return value
example_dict = Hasher()
print(example_dict['key1'])
# {}
print(example_dict['key1']['key2'])
# {}
print(type(example_dict['key1']['key2']))
# <class '__main__.Hasher'>
Note that this returns an empty Hasher when a key is missing.
Since Hasher is a subclass of dict you can use a Hasher in much the same way you could use a dict. All the same methods and syntax is available, Hashers just treat missing keys differently.
You can convert a regular dict into a Hasher like this:
hasher = Hasher(example_dict)
and convert a Hasher to a regular dict just as easily:
regular_dict = dict(hasher)
Another alternative is to hide the ugliness in a helper function:
def safeget(dct, *keys):
for key in keys:
try:
dct = dct[key]
except KeyError:
return None
return dct
So the rest of your code can stay relatively readable:
safeget(example_dict, 'key1', 'key2')
Answer from unutbu on Stack OverflowYou could use get twice:
example_dict.get('key1', {}).get('key2')
This will return None if either key1 or key2 does not exist.
Note that this could still raise an AttributeError if example_dict['key1'] exists but is not a dict (or a dict-like object with a get method). The try..except code you posted would raise a TypeError instead if example_dict['key1'] is unsubscriptable.
Another difference is that the try...except short-circuits immediately after the first missing key. The chain of get calls does not.
If you wish to preserve the syntax, example_dict['key1']['key2'] but do not want it to ever raise KeyErrors, then you could use the Hasher recipe:
class Hasher(dict):
# https://stackoverflow.com/a/3405143/190597
def __missing__(self, key):
value = self[key] = type(self)()
return value
example_dict = Hasher()
print(example_dict['key1'])
# {}
print(example_dict['key1']['key2'])
# {}
print(type(example_dict['key1']['key2']))
# <class '__main__.Hasher'>
Note that this returns an empty Hasher when a key is missing.
Since Hasher is a subclass of dict you can use a Hasher in much the same way you could use a dict. All the same methods and syntax is available, Hashers just treat missing keys differently.
You can convert a regular dict into a Hasher like this:
hasher = Hasher(example_dict)
and convert a Hasher to a regular dict just as easily:
regular_dict = dict(hasher)
Another alternative is to hide the ugliness in a helper function:
def safeget(dct, *keys):
for key in keys:
try:
dct = dct[key]
except KeyError:
return None
return dct
So the rest of your code can stay relatively readable:
safeget(example_dict, 'key1', 'key2')
By combining all of these answer here and small changes that I made, I think this function would be useful. its safe, quick, easily maintainable.
def deep_get(dictionary, keys, default=None):
return reduce(lambda d, key: d.get(key, default) if isinstance(d, dict) else default, keys.split("."), dictionary)
Example :
from functools import reduce
def deep_get(dictionary, keys, default=None):
return reduce(lambda d, key: d.get(key, default) if isinstance(d, dict) else default, keys.split("."), dictionary)
person = {'person':{'name':{'first':'John'}}}
print(deep_get(person, "person.name.first")) # John
print(deep_get(person, "person.name.lastname")) # None
print(deep_get(person, "person.name.lastname", default="No lastname")) # No lastname
Hey all, hoping someone can help me out. I'm new to python but getting the hang of it. I'm using subprocess to run ffprobe and have it return JSON. Depending on the file I'm analyzing, some properties may not exist and won't appear in the JSON results. If i print this it works:
dvd_properties['streams'][0]['duration']
But I'm having trouble using the get() method on it:
dvd_properties.get('streams').get('0').get('duration'), "Unknown"
I'm trying to use get() so I have a default value if there are no results in the JSON output. I'm testing on a file that does have the duration element to make sure it works if I have data.
Can anyone lend me a hand? I've tried to search but I can't find anything about this particular situation. I have the get('parent').get('child') working for simple cases, so I'm hoping it will work here as well and I'm just missing something obvious.
Thanks in advance, I do appreciate it.
Accessing first 10 key-value pairs in nested dictionary, creating a new nested dictionary with them.
python - Getting a key's value in a nested dictionary - Code Review Stack Exchange
Dict built-in get/set methods for nested keys - Ideas - Discussions on Python.org
python - Accessing value inside nested dictionaries - Stack Overflow
Great job so far! I have a few suggestions for your code:
Avoid writing functions with side effects such as printed output. Side effects make a function much less reusable. Instead, you may return a generator that
yields the next entry. This gives the caller maximum control over what to do with the result set: print it, iterate one by one, just get the first few items without searching the entire structure, etc.Consider adhering more strictly to Python naming conventions. For example, functions should be lower_camel_cased. Since your function returns multiple PLUs, the function name seems more accurately written as
get_plus. You can remove thepasskeyword in your function and pay close attention to spacing, for example in theprint (data[item])function call and yourelse :block.Clean up lines like
else : if item == 'PLU': print (data[item])Which is logically equivalent to:
elif item == 'PLU': print(data[item])Use
data.items()to iterate through your dictionary's keys and values rather than only keys. This allows cleaner syntax to access values.Make your function more general for maximum reusability. Fetching a list of values by key from a dictionary is a task that should work for any key. Why not make "PLU" a search parameter? If you want to keep the original
get_plusversion, write both functions and haveget_pluswrap the generalized version.isinstancemay be a more accurate choice thantypeif you wish to allow collection subclasses of dictionary to use your function.
Here's my version for consideration:
def find_by_key(data, target):
for key, value in data.items():
if isinstance(value, dict):
yield from find_by_key(value, target)
elif key == target:
yield value
def main():
menu = {
'PLU' : '234',
'Salad': {
'salad': {
'ceaser':{
'PLU': '32'
},
'italian':{
'PLU': '33'
}
}
},
'Dessert': {
'cookie': {
'PLU': '334',
'NAME': 'cookie ',
}
},
'Appetizer': {
'extra sauce': {
'PLU': '61',
'NAME': 'extra sauce',
}
}
}
for x in find_by_key(menu, "PLU"):
print(x)
if __name__ == '__main__':
main()
Your code is well organized and read well but there are a few oddities:
- Read PEP8 and apply official coding conventions so that your code read like Python code;
- Remove that useless
pass; - Don't
printyour results in the function that compute them, insteadreturnthem to the caller so your function is reusable (in your case,yielding them might be more appropriate); - Don't check for a specific type using
type: favorisinstance; better would be to not check for a type at all but for a feature: call theitemsmethod of what would appear to be adictand work with that, discard anything else that raise anAttributeError; - A more generic function would accept the key to search for as parameter;
- A recursive approach cannot handle arbitrary large structures due to the recursion limit, an iterative approach can (even though it would rarely be an issue in practice).
Proposed improvements:
def retrieve_nested_value(mapping, key_of_interest):
mappings = [mapping]
while mappings:
mapping = mappings.pop()
try:
items = mapping.items()
except AttributeError:
# we didn't store a mapping earlier on so just skip that value
continue
for key, value in items:
if key == key_of_interest:
yield value
else:
# type of the value will be checked in the next loop
mappings.append(value)
def main():
menu = {...}
for plu in retrieve_nested_value(menu, 'PLU'):
print(plu)
if __name__ == '__main__':
main()
As always in python, there are of course several ways to do it, but there is one obvious way to do it.
tmpdict["ONE"]["TWO"]["THREE"] is the obvious way to do it.
When that does not fit well with your algorithm, that may be a hint that your structure is not the best for the problem.
If you just want to just save you repetative typing, you can of course alias a subset of the dict:
>>> two_dict = tmpdict['ONE']['TWO'] # now you can just write two_dict for tmpdict['ONE']['TWO']
>>> two_dict["spam"] = 23
>>> tmpdict
{'ONE': {'TWO': {'THREE': 10, 'spam': 23}}}
My implementation:
def get_nested(data, *args):
if args and data:
element = args[0]
if element:
value = data.get(element)
return value if len(args) == 1 else get_nested(value, *args[1:])
Example usage:
>>> dct={"foo":{"bar":{"one":1, "two":2}, "misc":[1,2,3]}, "foo2":123}
>>> get_nested(dct, "foo", "bar", "one")
1
>>> get_nested(dct, "foo", "bar", "two")
2
>>> get_nested(dct, "foo", "misc")
[1, 2, 3]
>>> get_nested(dct, "foo", "missing")
>>>
There are no exceptions raised in case a key is missing, None value is returned in that case.
I don't know why am I struggling with this, but I am.
I have some data that is a dictionary of dictionaries:
data = {'0': {'account_id': None, 'hero_id': 17, 'player_slot': 0}, '1':
{'account_id': 12345678, 'hero_id': 37, 'player_slot': 1}, ...}What I want to be able to do is grab each account_id . For some reason I can't seem to figure out how to do this.
The closest thing I have been able to do that works, in the sense it runs but isn't actually what I want, is:
for x in data:
print(x)
# it prints:
> 0
> 1
> ...If I do the following:
for x in data:
print(x['account_id'] # TypeError: string indices must be integers
# or
print(x[0]) # IndexError: string index out of rangeThis is pretty much all I can think to do.
There is clearly something I am not understanding; please can someone tell me what that is and how I should actually be going about this problem?
dict.get accepts additional default parameter. The value is returned instead of None if there's no such key.
print(myDict.get('key1', {}).get('attr3'))
There is a very nice blog post from Dan O'Huiginn on the topic of nested dictionaries. He ultimately suggest subclassing dict with a class that handles nesting better. Here is the subclass modified to handle your case trying to access keys of non-dict values:
class ndict(dict):
def __getitem__(self, key):
if key in self: return self.get(key)
return self.setdefault(key, ndict())
You can reference nested existing keys or ones that don't exist. You can safely use the bracket notation for access rather than .get(). If a key doesn't exist on a NestedDict object, you will get back an empty NestedDict object. The initialization is a little wordy, but if you need the functionality, it could work out for you. Here are some examples:
In [97]: x = ndict({'key1': ndict({'attr1':1, 'attr2':2})})
In [98]: x
Out[98]: {'key1': {'attr1': 1, 'attr2': 2}}
In [99]: x['key1']
Out[99]: {'attr1': 1, 'attr2': 2}
In [100]: x['key1']['key2']
Out[100]: {}
In [101]: x['key2']['key2']
Out[101]: {}
In [102]: x['key1']['attr1']
Out[102]: 1
In other answers, you were pointed to how to solve your task for given dicts, with maximum depth level equaling to two. Here is the program that will alows you to loop through key-value pair of a dict with unlimited number of nesting levels (more generic approach):
def recursive_items(dictionary):
for key, value in dictionary.items():
if type(value) is dict:
yield from recursive_items(value)
else:
yield (key, value)
a = {'a': {1: {1: 2, 3: 4}, 2: {5: 6}}}
for key, value in recursive_items(a):
print(key, value)
Prints
1 2
3 4
5 6
That is relevant if you are interested only in key-value pair on deepest level (when value is not dict). If you are also interested in key-value pair where value is dict, make a small edit:
def recursive_items(dictionary):
for key, value in dictionary.items():
if type(value) is dict:
yield (key, value)
yield from recursive_items(value)
else:
yield (key, value)
a = {'a': {1: {1: 2, 3: 4}, 2: {5: 6}}}
for key, value in recursive_items(a):
print(key, value)
Prints
a {1: {1: 2, 3: 4}, 2: {5: 6}}
1 {1: 2, 3: 4}
1 2
3 4
2 {5: 6}
5 6
Here is code that would print all team members:
for k, v in Liverpool.items():
for k1, v1 in v.items():
print(k1)
So you just iterate every inner dictionary one by one and print values.