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.
Related
I have a JSON / Python-dictionary object with some values in a numeric format, e.g. {'id': 'xxx-xxx-xxx', 'property_type': 930, ...}.
What I wish to do, is when I display the object on my website, I want to translate 930 into what it actually means, e.g. 'Public institutional building'.
As these objects come from an API call, there are quite a lot I need to translate, and it is not necessarily for each individual key-value pair I need to translate something. I guess this is common practice to do when working with APIs, however, I do not seem to be able to guide myself in the right direction of any best-practise. I have lots of ideas on how to solve it, but not in any way that I believe is considered 'best-practice'.
Obviously, I would likely have to build up an additional dictionary that would look something like:
{'property_type': {120: 'Private property', 240: 'Vacation property' ...}, 'roof_type': {10: 'xxx', 20: 'xxx'}}
But what would then be the most convenient way to thereafter automatically take any given dictionary, loop through the 'translation-dictionary' and if there is a match, then translate the dictionary.
In most cases, the object I'll be working with will be nested, so I also need to check the next level sometimes.
This funtion can be used to Recursively search through a nested dictionary and return what we find.
my_dict = {'property_type': {120: 'Private property', 240: 'Vacation property'}, 'roof_type': {10: 'xxx', 20: 'xxx'}}
def recursive_search(dictionary, item):
# Recursively check for a key in a dictionary
if item in dictionary: # Check if item is in current dict
return dictionary[item] # our property value
for key, value in dictionary.items():
if isinstance(value, dict): # Check if we have a nested dict
result = recursive_search(value, item) # Check the next level
if result is not None: # Make sure we don't return early
return result
print(recursive_search(my_dict, 120))
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__ ]
I have a dictionary and I would like to get some values from it based on some keys. For example, I have a dictionary for users with their first name, last name, username, address, age and so on. Let's say, I only want to get one value (name) - either last name or first name or username but in descending priority like shown below:
(1) last name: if key exists, get value and stop checking. If not, move to next key.
(2) first name: if key exists, get value and stop checking. If not, move to next key.
(3) username: if key exists, get value or return null/empty
#my dict looks something like this
myDict = {'age': ['value'], 'address': ['value1, value2'],
'firstName': ['value'], 'lastName': ['']}
#List of keys I want to check in descending priority: lastName > firstName > userName
keySet = ['lastName', 'firstName', 'userName']
What I tried doing is to get all the possible values and put them into a list so I can retrieve the first element in the list. Obviously it didn't work out.
tempList = []
for key in keys:
get_value = myDict.get(key)
tempList .append(get_value)
Is there a better way to do this without using if else block?
One option if the number of keys is small is to use chained gets:
value = myDict.get('lastName', myDict.get('firstName', myDict.get('userName')))
But if you have keySet defined, this might be clearer:
value = None
for key in keySet:
if key in myDict:
value = myDict[key]
break
The chained gets do not short-circuit, so all keys will be checked but only one used. If you have enough possible keys that the extra lookups matter, use the for loop.
Use .get(), which if the key is not found, returns None.
for i in keySet:
temp = myDict.get(i)
if temp is not None:
print temp
break
You can use myDict.has_key(keyname) as well to validate if the key exists.
Edit based on the comments -
This would work only on versions lower than 3.1. has_key has been removed from Python 3.1. You should use the in operator if you are using Python 3.1
If we encapsulate that in a function we could use recursion and state clearly the purpose by naming the function properly (not sure if getAny is actually a good name):
def getAny(dic, keys, default=None):
return (keys or default) and dic.get(keys[0],
getAny( dic, keys[1:], default=default))
or even better, without recursion and more clear:
def getAny(dic, keys, default=None):
for k in keys:
if k in dic:
return dic[k]
return default
Then that could be used in a way similar to the dict.get method, like:
getAny(myDict, keySet)
and even have a default result in case of no keys found at all:
getAny(myDict, keySet, "not found")
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)
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