One question about using dictionary get function in python. I understand get function can provide default value for dict if key does not exist. What if during the program, we know key must be existed. like following code. should we still use get function or we can just use dict[key] to get value. does it mean get function can replace dict[key]
value = 'default'
dict_get = dict(key='value')
def test_get(dict_get):
return dict_get.get('key', 'default_value')
test_get.get('key')
test_get['key']
If the key must exist, you should use yourdict[key], i.e. the __getitem__ method.
If for some reason the key does not exist, you want your program to crash with a KeyError, because clearly there's something wrong with your program that needs to be fixed.
If the key should exist, but may not due to reasons other than faulty program logic, you can take a more defensive approach. For example, if a user is queried to input a valid key but fails to do so, you could fall back to a default value using dict.get or ask the user again.
dict.get will return None if the key doesn't exist. In some applications you just want to raise an exception there instead, which is what dict[key] does. For example:
scores = {"Larry": 5}
bob_score = scores.get("Bob") # None
if bob_score:
print("You're doing great Bob!")
else:
print("Too bad Bob, you're out.")
Our code assumes that the key Bob is in the dictionary, and maps to an integer. Even though Bob isn't in the dictionary because None is a falsy value we still get a meaningful answer from this code, even though that conclusion is based on a mistake. It would be much better for an exception to be raised here.
Related
This is a question for one of my practice questions but I do not even know how to begin this let alone complete it.
you'll write a safe version of testing a dictionary for a specific key and extracting the corresponding element. Normally, if you try to access a key that does not exist, your program crashes with a KeyError exception. Here, you'll write a function which:
is named safe_access
takes 3 arguments: a dictionary, a key, and a default return value
returns 1 value: the value in the dictionary for the given key, or the default return value
The third argument to this function is a default argument, whose initial value should be the Python keyword None. Your task in this function is three-fold: Try to access the value in the dictionary at the given key (wrap this in a try-except statement) If the access is successful, just return the value If the access is NOT successful (i.e. you caught a KeyError), then return the default value instead You must include a try-except statement in your function!
I have a view that simply checks for a key in the session store and if its present it will delete it and if its not present it should pass, it may be worth noting that the key store is holding ids of model instances.
def RefreshInstances(request):
if request.session['instances']:
del request.session['instances']
else:
pass
return redirect('/')
This works and achieves its goal deleting the instances, however if the key store is empty I get a key error rather than the code just being passed?
Can anyone shed any light?
Thanks in advance.
Accessing keys of dicts (or dictlikes) that don't exist do raise KeyError.
You can explicitly check for the key:
if 'instances' in request.session:
# ...
or you can use the .get() method, which returns a default value (None by default) if the key does not exist – this is also handy because falsy values such as 0, '', False, [] etc. pass the test:
if request.session.get('instances'):
del request.session['instances']
... but for deletion, simply use .pop() with a default and without an if:
request.session.pop('instances', None) # Remove `instances` if it's there, do nothing otherwise.
This can be condensed into a single line:
request.session.pop('instances', None)
If you use request.session['instances'], you perform a lookup. If the key (here 'instances') is not available, it will raise a KeyError. The error is thus thrown before the expression's truthiness is evaluated by the if statement.
It is however better to just use .pop(..) here:
request.session.pop('instances', None)
This will remove the key if it is available, and otherwise do nothing. It will return the value that was associated with the 'instances' key, given such value exists, and return None otherwise.
I am receiving from an API a dictionary, but the problem is that sometimes I get {value: test} and others {key: test}. I am using a try/except block to take the one set:
try:
var = received_dict['value']
except KeyError:
var = received_dict['key']
Is there a better way to do that in Python 3?
received_dict.get('value', received_dict.get('key', False))
This will still throw an error if neither key is present which is almost certainly the desired behavior.
received_dict.get('value', received_dict['key'])
However, python generally uses the: Easier to ask for forgiveness than permission mentality. If one of those occurs more frequently than the other how you're currently doing it may be best. If it really is 50/50 however using a get might be a cleaner way to go.
https://docs.python.org/3.4/glossary.html (See EAFP)
To be honest your approach seems good. If you want a bit more control over the different cases you can do this:
vars = [received_dict[key] for key in ["value","key"] if key in received_dict.keys()]
if not vars:
raise KeyError("No 'value' or 'key' keys exist in dictionary")
elif len(vars)==2:
raise KeyError("Both 'value' and 'key' keys exist in dictionary")
else:
var=vars[0]
The only advantage of this approach is that it throws an error also when both keys exist. If you don't care about the "both exist" error case and you don't mind a different type of error being raised when the "none exist" error case happens, then you can do the code below to have a one liner:
var = [received_dict[key] for key in ["value","key"] if key in received_dict.keys()][0]
I have problems to apply clean code principles to this code section.
In clean code principles is written, that a function should do one thing and do it well.
But in this case I do not know how to refractor this function according to those principles without decreasing performance.
for netobject in netObjectList:
for key, value in netobject.getObjectParams().items():
if value.getValue() == '' and key is not 'comment':
Errorhandler().error_on_pos(value.getRow(), value.getCol(),
str(key) + ' is missing')
else:
#do syntax check on values in the dictionary
NValidator.checkSyntaxOfValue(key, value)
A netobject is dictionary containing all its parameters.
So I know that this function does more than one thing. And I think it is quite hard to unittest this function.
But if I proof syntax and missing parameters separatley, I have to iterate twice over all network objects bundled in that neObjectList. But otherwise it is no clean code to check syntax and missing parameters in one function.
I very often have these inner conflicts, when I am writing code.
Do you have some tips or suggestions to fix these problems?
If you need more description for that code section, please let me know.
Errorhandler is just a way to print out my errors, if an missing parameter is found.
The function checkSyntaxOfValue(key, value) calls suiteable check methods depending on the key of the bundled parameters. I think there would be also a better solution to handle syntax checks. But I do not know.
As far as I'm concerned, your code IS "doing one thing": validating your netobjects values. The fact that the validation is done in two steps (first checking if there's a value where expected, then checking the value's 'syntax') is an implementation detail.
Now what can be questionned is whether checking for empty values is distinct from checking the value's syntax - ie if an empty value is not a valid syntax for a given key, shouldn't the check be a responsability of NValidator.checkSyntaxOfValue ?
Also if for whatever reason you really do have to keep the tests (empty value / value syntax) distinct, you should probably encapsulate the emptyness test in a distinct function so you have the same level of abstraction for both tests, ie:
def checkForNotEmptyValue(key, value):
if key == 'comment':
# we don't care about empty values here
return True
if value.getValue() == '':
Errorhandler().error_on_pos(
value.getRow(),
value.getCol(),
'{} is missing'.format(key)
)
return False
return True
and then
for netobject in netObjectList:
for key, value in netobject.getObjectParams().items():
if checkForNotEmptyValue(key, value):
NValidator.checkSyntaxOfValue(key, value)
NB: this is just an example based on your code snippet and question - I don't have enough context to tell whether it's the "best" solution here.
As a last word: don't get over the top with "best practices" either. Writing clean code IS important, but "clean" is a relative notion, and trying to strictly follow all "golden rules" is downright impossible (specially since no one really agrees on those rules xD).
Actually, the point of "golden rules" etc is to make you aware of what problem you might have if you don't follow the rule - once you know, it's your responsability to decide when the rule applies and when it's doesn't. It's like raising children: at first you just forbid them to cross the road alone because they are too young to really understand the dangers. Once they are mature enough to get the point, the rule becomes useless.
Let me comment on your code first:
for netobject in netObjectList:
for key, value in netobject.getObjectParams().items():
"""
Below you seem to be assuming that first item must have 'comment' as key?
What if the key 'comment' actually exists? If you iterate like this you will be
Throwing errors until you iterate through all the items (and either hit the required key or not)
"""
if value.getValue() == '' and key is not 'comment':
Errorhandler().error_on_pos(value.getRow(), value.getCol(), str(key) + ' is missing')
else:
NValidator.checkSyntaxOfValue(key, value)
So, I'd choose a different approach. If you definitely want to work with item having key 'comment'
(and assuming here that your keys are unique - ie no multiple 'comment' keys in a single netobject object) you could do this instead:
for netobject in netObjectList:
try:
# try to get the value directly
# (of course you could use .keys() instead of .items() to evaluate existing keys first)
value = netobject.getObjectParams()['comment']
NValidator.checkSyntaxOfValue('comment', value)
except:
# Due to KeyError we now know for sure that such key definitely doesn't exist
# So we can handle the Exception, or throw your own such as:
Errorhandler().error_on_pos(...)
This example will either throw you just one True (ie not false positive) error, or it will succeed with syntax check
EDIT:
Decided to illustrate with better example:
Here I'm first emulating your netobject
class netobject_class(object):
def __init__(self, datadict):
self.data = datadict
def getObjectParams(self):
return self.data
def __str__(self):
return "I'm a netobject_class instance (hash=" + str(self.__hash__()) + ") containing a data Dict"
Now we create the netObjectList which contains 3 sample netobject_class instances. Note that second Dict has no 'comment' key.
netObjectList = [
netobject_class({'comment': 'comment1'}),
netobject_class({'somekey':'somevalue','somekey2':'somevalue2'}),
netobject_class({'comment': 'comment2'})
]
Now we run the code:
print("-" * 40)
for netobject in netObjectList:
# print("Working with netobject: %s" % netobject)
try:
# try to get the value directly
# (of course you could use .keys() instead of .items() to evaluate existing keys first)
value = netobject.getObjectParams()['comment']
print("****** value='%s' # aka comment" % value)
#NValidator.checkSyntaxOfValue('comment', value)
except Exception as e:
# Due to KeyError we now know for sure that such key definitely doesn't exist
# So we can handle the Exception, or throw your own such as:
print('Error: key', str(e), 'does not exist! Skipping....')
print("-" * 40)
And this produces:
----------------------------------------
****** value='comment1' # aka comment
----------------------------------------
Error: key 'comment' does not exist! Skipping....
----------------------------------------
****** value='comment2' # aka comment
----------------------------------------
If an associative array exists in Python3, is it a waste to check if an element of it exists rather than just using it?
Should you:
if 'name' in array and not array['name'].startswith('something'):
Or should you just:
if not array['name'].startswith('something'):
... And will Python3 handle it "for" you?
You can do -
if not array.get('name', 'something').startswith('something'):
get() function returns the second value by default if the key ( name ) is not found.
So in above case , this would return something , if key is not found in the dictionary, and because of the .startwith() and the not , the complete conditional expression would evaluate to False , as it would be doing in OP's first example .
The answer really depends on where does your array come from. Also on what can you do as a result of the check.
For example if it comes from the user, then yes, you definitely should check if name exists, so that you can respond with a better error message than some big stacktrace.... KeyError: "name".
If it's an internal structure you created and it's not exposed for user modifications, or if it's exposed via API but the expectation is that nobody should touch it, then don't bother checking. If it's missing, then it's an internal exception the developers should see and fix.
Of course there may be a situation where you don't really care if it was provided or not, because you have a reasonable fallback. Then a default value like in Anand's answer is a good solution too. array.get('name', some_default)
Python won't handle it for you. You'll have to do some work in both cases. Try to write code that makes sense to you later, because you'll write it once and read it (and maintain it) many times over.
You can do it two ways, pick the one that makes the most sense to you:
obj = array.get('name')
if obj and not obj.startswith('something'):
pass
In your second option, you'll have to catch the exception:
try:
if not array['name'].startswith('something'):
pass
except KeyError:
print('name does not exist in array')
Just do ...
if not array.get('name',"").startswith('something')
If name exists then it returns array['name'] else it will return empty string .