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.
Related
I've made a program that fetches data from an api and then uses some keys of it. It should return the keys, but if a certain key has no value- or is not in the data I want it to return that key with a 0.
So for example when looking up a certain player's karma (karma = r.json()["player"]["karma"]) It will return a NoneType error as the value is empty. How can I make it set that value to 0, without writing a dozen for loops checking each one individually?
What I don't want (as there are a lot of keys):
try:
karma = r.json()["player"]["karma"]
except NoneType:
karma = 0
Well you just discover the get method.
karma = r.json()["player"].get("karma", 0)
The first argument is the key you want the value.
The second argument is the default value to return if the key does not exist, by default its value is None
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
----------------------------------------
I'm trying to remove null and empty keys from my python object by calling a method from a module.
from file2 import remove_nulls
# initialize object and set attributes
obj.remove_nulls()
In my remove_nulls() method, if I print the resultant object, I can observe that null and empty keys are removed, but after returning from the function, the final object still has the null and empty keys.
def remove_null(feature):
return json.dumps(del_none(feature.__dict__.copy()))
def del_none(d):
for key, value in list(d.items()):
if value is None:
del d[key]
elif isinstance(value, dict):
del_none(value)
return d
Can someone help to fine where it went wrong?
Just too many copies...:
def remove_null(feature):
return json.dumps(del_none(feature.__dict__.copy()))
that applies del_none on a copy of your object, dumps the proper "cleaned" object, then returns, leaving your object untouched (since you created a copy). You probably need to do just:
def remove_null(feature):
return json.dumps(del_none(feature.__dict__))
The confusion probably comes from the need to copy the dictionary keys+values to avoid removing items on a dict while iterating on it (which was probably handled here, at the wrong level, then handled at the proper level, but the first copy was not removed)
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.
I have a dictionary defined as
BREED_CLASS = {
'bdcl': ['Border Collie', BreedBdcl, 'BreedBdcl'],
}
and a model defined as
class Dog(models.Model):
breed_code = models.CharField(max_length=4, default='')
I'm trying to use the breed_code as a key to access the items in the dictionary, for example like this
return BREED_CLASS[instance.breed_code][0]
but that raises a KeyError, with exception value '' aka the default value.
I've tried doing something like
bc = instance.breed_code
and that correctly returns the value saved to that instantiated object ('bdcl'). But when I put that into the dictionary access as so
return BREED_CLASS[bc][0]
it gets me the same exact KeyError with the same exact exception value. How do I figure this out?
The CharField class seems to be using the __str__ magic method to return a string when you print it, giving you the illusion it is a string but it's actually an object. Your dictionary uses the actual string for storing the value but when you use BREED_CLASS[instance.breed_code] you are passing it the object. Converting your object to a string should fix it. Try this:
BREED_CLASS[str(instance.breed_code)][0]
So I figured out a workaround that gets me what I want, but I have no idea why it works.
This by itself does not work
return BREED_CLASS[instance.breed_code][2]
But having this for loop that iterates through the keys does
for key in BREED_CLASS:
if key == instance.breed_code:
return BREED_CLASS[instance.breed_code][2]
Notice that both return calls are identical, with instance.breed_code being used for the dict key index.
Does anyone have an answer as for why this is?