Accessing dictionary of dictionary in python - python

Hi in my code there is a dictionary of dictionary.
nrec={'bridge': 'xapi1', 'current_operations': {}, 'uuid': '9ae5ca7d-e7d6-7a81-f619-d0ea33efb534', 'tags': [], 'other_config': {'is_guest_installer_network': 'true', 'netmask': '255.255.255.0', 'ip_end': '192.168.128.254', 'ip_begin': '192.168.128.1'}, 'name_label': 'Guest installer network', 'VIFs': ['OpaqueRef:dff106aa-1a94-8384-1c86-862b47c87fcf'], 'allowed_operations': [], 'PIFs': [], 'name_description': 'Network on which guests will get assigned a private local IP address', 'MTU': '1500', 'blobs': {}}
Here you can see inside this dictionary one more dictionary 'other_config': {'is_guest_installer_network': 'true', 'netmask': '255.255.255.0', 'ip_end': '192.168.128.254', 'ip_begin': '192.168.128.1'} is there.
I want to check is_guest_installer_network=="true"
I have done nrec["other_config"]["is_guest_installer_network"]== "true" but the problem is some attribute have this other_config property with either empty value or different value. Then in this case my solution will throw exception. So i want to do it in a efficient way like If is_guest_installer_network is consists in the dictionary and the value (string) is true or not.

If this is a config item, you shouldn't need to access it very often (thus your efficiency requirement would be questionable). Configure once and forget about it (e.g. set self.is_guest_installer_network = True).
If you can't forget about it, it would depend on the likelihood of the entry being present in your dictionary. If it's more likely that the item is missing it would probably be better if you do something like the following. You get some shortcut behavior if an item misses, the other config dict is looked up only once (for the existance check and for the value following lookup.
def check_guest_installer_network(nrec):
other_config = nrec.get("other_config", None)
return other_config is not None and other_config.get('is_guest_installer_network', False)
If it's more likely that the item is there, the lazy try/except approach could be better suited. As the saved check performance, would outweigh the additional performance cost when the exception actually needs to be handled.
def check_guest_installer_network(nrec):
try:
return nrec["other_config"]['is_guest_installer_network'] == "true"
except KeyError:
return False
After all, if this check indeed has a significant impact on the overall performance, you should put this variable somewhere it is better accessible, than in a nested dictionary, e.g. put it into a global/class member variable once, and enjoy the cheap and easy checks afterwards.
You should have a look at the cprofile module to verify that this lookup is indeed the bottleneck of your software, that is worth the optimization effort. And You should look at the timeit module to choose the most performant solution for your problem.

Try this:
nrec["other_config"].get('is_guest_installer_network')
It'll return its value if 'is_guest_installer_network' exists in nrec['other_config']

Just check
'other_config' in nrec and 'is_guest_installer_network' in nrec['other_config'] and nrec['other_config']['is_guest_installer_network'] == 'true'

To check if a key exists in a dictionary use:
if 'key' in dictionary:
# do sth
So your code will be:
if 'other_config' in nrec and 'is_guest_installer_network' in nrec['other_config'] and nrec['other_config']['is_guest_installer_network'] == 'true':
# do sth
Additionally if you want default value if the key is not present in the dict use get(key, default) method:
nrec.get('other_config', default_value_here)

One possible solution is
>>> try:
if nrec["other_config1"]["is_guest_installer_network"] == 'true':
print 'Do Something'
except (KeyError,TypeError):
None #Or do something logical

Your best bet to avoid exception is either try .. except, or use dictionary built-in methods.
my_dict = {'one': {'two': 'hello world!'}, 'four': 'Dummy!'}
try:
my_name = my_dict['one']['two']
except:
pass
// instead of pass, you can also return something or do something else
try:
my_dict['one']['two']
except Exception as e:
my_name = 'default'
return my_name, e // returns a tuple which contains 'default' and error message
#or use has_key()
# this is for one-level nested dictionary
for id, items in my_dict.iteritems():
if items.has_key('two'):
//do something
# or simply this --> eliminates dummy for loop
if my_dict['one'].has_key('two'): // has_key returns True / False
// do something
# or use in operator (replace has_key)
if 'two' in my_dict['one'].keys():
// do something
# or get method
my_dict['one'].get('two', 'Default')
Get is nice if that's all you need to avoid exception.

You've got the answer
nrec["other_config"]["is_guest_installer_network"]== "true"
can be written like
if nrec.has_key("other_config") and type(nrec["other_config"]) == type({}) and nrec["other_config"].has_key("....") and nrec["other_config"]["is_guest_installer_network"]== "true":
But this is sort of ugly.
Or, as noted in the comments
nrec.get("other_config",{}).get("is_guest_installer_network",{}) == "true"
But this doesn't handle the type checking.
Maybe best to do something like this
def traverse(_dict, keys):
val = _dict
for key in keys[:-1]:
if key in val and type(val[key]) is dict :
val = val[key]
else:
return None
return val.get(keys[-1],None)

Related

Is there a better way to parse a python dictionary?

I have a json dictionary and I need to check the values of the data and see if there is a match. I am using multiple if statements and the in operator like so:
"SomeData":
{
"IsTrue": true,
"ExtraData":
{
"MyID": "1223"
}
}
json_data = MYAPI.get_json()
if 'SomeData' in json_data:
some_data = json_data['SomeData']
if 'IsTrue' in some_data:
if some_data['IsTrue'] is True:
if 'ExtraData' in some_data:
if 'MyID' in some_data['ExtraData']:
if some_data['ExtraData']['MyID'] == "1234":
is_a_match = True
break
I know that in python3 the in operator should be used, but I am thinking there must be a better way than using multiple if statements like I am using.
Is there a better way to parse json data like this?
Yes, you can assume that the keys are present, but catch a KeyError if they aren't.
try:
some_data = json_data['SomeData']
is_a_match = (
some_data['IsTrue'] is True and
some_data['ExtraData']['MyID'] == "1234"
)
except KeyError:
is_a_match = False
This style is called easier to ask for forgiveness than permission (EAFP) and it's used a lot in Python. The alternative is look before you leap (LBYL), which you use in your solution.
I would suggest writing a path function to access values in your nested dictionary. Something along the lines of this (pseudocode):
def get_path_value(json_dict, path):
"""
json_dict - dictionary with json keys and values
path - list of key sequences, e.g. ['SomeData', 'IsTrue']
you can make this a helper function and use an entry point that
splits paths, e.g. "SomeData/IsTrue"
"""
if len(path) == 1:
# last tag, base case
return json_dict[path[0]]
else:
return get_path_value(json_dict[path[0]], path[1:])
Add try/catch if you want something other than bad key, but this will let you navigat the dictionary a little more eloquently. Then you have things like:
if get_path_value(json_dict, ["SomeData", "IsTrue"]) == True and ...
You could even write a nice little class to wrap this all up, e.g. json["SomeData/IsTrue"] == True
Best of luck,
Marie

How to catch KeyError in a python for loop with lambda function?

Is it possible to catch a KeyError for x['uri'] for this particular scenario.
joined_rooms = ('coala/coala', 'coala/coala-bears')
room_data = [{'id': '130', 'uri': 'coala/coala'},
{'id': '234', 'name': 'Nitanshu'},
{'id': '897', 'uri': 'coala/coala-bears'}]
for room in filter(lambda x: x['uri'] in joined_rooms, room_data):
#do stuff
Doing this:
try:
for room in filter(lambda x: x['uri'] in joined_rooms, room_data):
#do stuff
except KeyError:
pass
will skip all the items after KeyError is raised.
Is there any way to handle this apart from migrating to the classic nested for loops with if condition?
Python dicts have a get method for just such an occasion. It takes two arguments, first is the key you're looking for, second is the default value to use if the key is not present in the dictionary. Try this:
for room in filter(lambda x:x.get('uri',None) in joined_rooms, room_data):
#do stuff
The problem here, although it might not be a problem in your case, is that you're left with needing to supply a default value that will never appear in joined_rooms.
As mentioned, you can use dict.get to return a default value if a key does not exist in a dictionary. If not provided, this default value is None.
As an alternative, you can use a generator expression. This will be more efficient than filter + lambda expression. It may also be more readable:
for room in (x for x in room_data if x.get('iri') in joined_rooms):
# do something
Another perfectly valid solution is to use an if statement within your for loop:
for room in room_data:
if 'iri' in room:
# do something

Cleaner way to represent Rules (if-else) in Python

I am trying to find a design pattern (or maybe an algorithm) which will help me write these rules in a cleaner way. Any suggestions?
def get_rules(user, value):
if 500 <= value < 5000 and not user.address:
return [REQUEST_ADDRESS]
if value >= 5000:
if not user.address and not user.phone:
return [REQUEST_ADDRESS, REQUEST_PHONE]
if user.address and not user.phone:
return [REQUEST_PHONE]
if not user.address and user.phone:
return [REQUEST_ADDRESS]
# Potentially ~20 more conditions here based on various attributes of user
return [STATES.REQUEST_NONE]
Note: I am not looking for a rules engine since I don't want to complicate my code by adding "business friendly" DSL in python. Python itself is a simple language to write these rules.
Interesting read: http://martinfowler.com/bliki/RulesEngine.html (but I am still trying to stay away from a "framework" to do this for me).
You're checking lots of different combinations with your "if a and not b else check not a and b else check not a and not b" strategy to figure out what combination of requests you need to send.
Instead, only check what you're missing:
missing = []
if not user.phone:
missing.append(REQUEST_PHONE)
if not user.address:
missing.append(REQUEST_ADDRESS)
return missing or [REQUEST_NONE]
You can use a dict in this case:
resdict = {(False, False): [REQUEST_ADDRESS, REQUEST_PHONE],
(True, False): [REQUEST_PHONE],
(False, True): [REQUEST_ADDRESS]}
return resdict[(user.address, user.phone)]
You can also use a list comprehension:
return [req for req, haveit in zip([REQUEST_ADDRESS, REQUEST_PHONE], [user.address, user.phone]) if not haveit]
Or a simpler list append:
res = []
if not user.address:
res.append(REQUEST_ADDRESS)
if not user.phone:
res.append(REQUEST_PHONE)
If I understood the question right, you have a list of attributes for the user. If one is false a REQUEST value schould be added to the list. Then this could help:
# define all your combinations here:
mapping = {'address': REQUEST_ADDRESS, 'phone': REQUEST_PHONE, …)
return [value for key, value in mapping.items()
if not getattr(user, key, None)]
Looks like your "rules" boil down to this: Request values for fields that are not present as attributes in the object user. I will assume that the mapping of attributes to requests can be arbitrary; you can represent it as a dictionary mapping, e.g. like this:
rulemap = {
"address": REQUEST_ADDRESS,
"phone": REQUEST_PHONE,
# etc.
}
You can then get a list of the requests to issue by checking which of the keys in rulemap are not present as attributes in the object user:
return [ rulemap[fld] for fld in rulemap.keys() if fld not in user.__dict__ ]

python dictionary check if any key other than given keys exist

Lets say I have a dictionary that specifies some properties for a package:
d = {'from': 'Bob', 'to': 'Joe', 'item': 'book', 'weight': '3.5lbs'}
To check the validity of a package dictionary, it needs to have a 'from' and 'to' key, and any number of properties, but there must be at least one property. So a dictionary can have either 'item' or 'weight', both, but can't have neither. The property keys could be anything, not limited to 'item' or 'weight'.
How would I check dictionaries to make sure they're valid, as in having the 'to', 'from', and at least one other key?
The only method I can think of is by obtaining d.keys(), removing the 'from' and 'to' keys, and checking if its empty.
Is there a better way to go about doing this?
must = {"from", "to"}
print len(d) > len(must) and all(key in d for key in must)
# True
This solution makes sure that your dictionary has more elements than the elements in the must set and also all the elements in must will be there in the dictionary.
The advantage of this solution is that, it is easily extensible. If you want to make sure that one more parameter exists in the dictionary, just include that in the must dictionary, it will work fine. You don't have to alter the logic.
Edit
Apart from that, if you are using Python 2.7, you can do this more succinctly like this
print d.viewkeys() > {"from", "to"}
If you are using Python 3.x, you can simply write that as
print(d.keys() > {"from", "to"})
This hack works because, d.viewkeys and d.keys return set-like objects. So, we can use set comparison operators. > is used to check if the left hand side set is a strict superset of the right hand side set. So, in order to satisfy the condition, the left hand side set-like object should have both from and to, and some other object.
Quoting from the set.issuperset docs,
set > other
Test whether the set is a proper superset of other, that is, set >= other and set != other.
if d.keys() has a length of at least 3, and it has a from and to attribute, you're golden.
My knowledge of Python isn't the greatest but I imagine it goes something like if len(d.keys) > 2 and d['from'] and d['to']
Use the following code:
def exists(var, dict):
try:
x = dict[var]
return True
except KeyError:
return False
def check(dict):
if exists('from', dict) == False:
return False
if exists('to', dict) == False:
return False
if exists('item', dict) == False and exists('weight', dict) == False:
return False
return True
def main():
d = {'from': 'Bob', 'to': 'Joe', 'item': 'book', 'weight': '3.5lbs'}
mybool = check(d)
print mybool
if __name__ == '__main__':
main()
This doesn't address the problem OP has, but provides what I think to be a better practice solution. I realize there's already been an answer but I just spent a few minutes reading on best practices and thought I would share
Problems with using a dictionary:
Dictionaries are meant to be on a key value basis. You inherently have 2 different types of key values given that to and from are mandatory while item and weight are optional
Dictionaries are meant to be logic-less. By setting certain requirements, you violate the principal of a dictionary which is just meant to hold data. To make a instance you need to build some sort of logic constructor for the dictionary
So why not just use a class? Proposed alternative:
class D(dict): # inheirits dict
def __init__ (self,t,f,**attributes): # from is a keyword
self['to'] = t
self['from'] = f
if(len(attributes) > 0):
self.update(attributes)
else:
raise Exception("Require attribute")
d = D('jim','bob',item='book')
print d # {'to': 'jim', 'from': 'bob', 'item': 'book'}
print d['to'] # jim
print d['item'] # item
print d['from'] # bob
d = D('jim','bob') # throws error
Obviously this falls apart if to and from are set asynchronously but I think the base idea still holds. Creating a class also gives you the verbosity to prevent to and from from being overwritten/deleted as well as limiting the minimum/maximum of attributes set.

Is it reasonable to use None as a dictionary key in Python?

None seems to work as a dictionary key, but I am wondering if that will just lead to trouble later. For example, this works:
>>> x={'a':1, 'b':2, None:3}
>>> x
{'a': 1, None: 3, 'b': 2}
>>> x[None]
3
The actual data I am working with is educational standards. Every standard is associated with a content area. Some standards are also associated with content subareas. I would like to make a nested dictionary of the form {contentArea:{contentSubArea:[standards]}}. Some of those contentSubArea keys would be None.
In particular, I am wondering if this will lead to confusion if I look for a key that does not exist at some point, or something unanticipated like that.
Any hashable value is a valid Python Dictionary Key. For this reason, None is a perfectly valid candidate. There's no confusion when looking for non-existent keys - the presence of None as a key would not affect the ability to check for whether another key was present. Ex:
>>> d = {1: 'a', 2: 'b', None: 'c'}
>>> 1 in d
True
>>> 5 in d
False
>>> None in d
True
There's no conflict, and you can test for it just like normal. It shouldn't cause you a problem. The standard 1-to-1 Key-Value association still exists, so you can't have multiple things in the None key, but using None as a key shouldn't pose a problem by itself.
You want trouble? here we go:
>>> json.loads(json.dumps({None:None}))
{u'null': None}
So yea, better stay away from json if you do use None as a key. You can patch this by custom (de/)serializer, but I would advise against use of None as a key in the first place.
None is not special in any particular way, it's just another python value. Its only distinction is that it happens to be the return value of a function that doesn't specify any other return value, and it also happens to be a common default value (the default arg of dict.get(), for instance).
You won't cause any run-time conflicts using such a key, but you should ask yourself if that's really a meaningful value to use for a key. It's often more helpful, from the point of view of reading code and understanding what it does, to use a designated instance for special values. Something like:
NoSubContent = SubContentArea(name=None)
{"contentArea":
{NoSubContent:[standards],
SubContentArea(name="Fruits"): ['apples', 'bananas']}}
jsonify does not support a dictionary with None key.
From Flask import jsonify
def json_():
d = {None: 'None'}
return jsonify(d)
This will throw an error:
TypeError: '<' not supported between instances of 'NoneType' and 'str'
It seems to me, the larger, later problem is this. If your process is creating pairs and some pairs have a "None" key, then it will overwrite all the previous None pairs. Your dictionary will silently throw out values because you had duplicate None keys. No?
Funny though, even this works :
d = {None: 'None'}
In [10]: None in d
Out[10]: True

Categories

Resources