i'm having trouble with ValueErrors in python - python

I'm trying to create a class "Hippocrates" with a function inside that lets me create a dictionary from a given text document (the content of the document is not important to my issue). When I try to return a value from the dictionary and the key does not exist in said dictionary, I want to raise a ValueError stating "invalid ICD-code". I then want the code to continue running since I need it to be able to keep returning values one after each other, but since I raise a ValueError the code stops.
I tried putting it inside a try except block but I'm not too familiar with it yet so I'm struggling.
Here's what I have so far:
class Hippocrates:
def __init__(self, location):
self.file = open(location, "r")
def description(self, code):
answer = {}
data = self.file.readline()
while data:
current = data.split()
x = current.pop(0)
current = ' '.join(current)
answer[x] = answer.get(current, current)
data = self.file.readline()
try:
return answer[code]
except ValueError:
print('invalid ICD-code')
When I try getting a value from it with an invalid key I get a KeyError. I don't know why this would happen since it should just go straight to a ValueError.
I do get the correct value when I use a valid key.
Can someone help me figure this out please?

You should except KeyError for invalid keys not ValueError
so just change the try/except to:
try:
return answer[code]
except KeyError:
print('invalid ICD-code')

The reason you're receiving a KeyError rather than a ValueError is that Python raises a KeyError when you attempt to get data for a key which doesn't exist in the dictionary. Therefore, no ValueError is raised and your catch block is never entered. The simple solution to this would be to change the type of exception you're trying to catch to KeyError. However, using exception handling as execution flow is generally a bad practice so I would suggest checking if the key exists in the dictionary before retrieving it:
if code in answer:
return answer[code]
else:
raise ValueError('invalid ICD-code')
As an aside, I would suggest that you avoid having an open file as a field on your class. In general, it's a good idea to open files, read them and close them as one operation. Also, your code is attempting to read the data in the file every time you try to access a code from the dictionary, so this will only work for the first code. Instead, you should try this:
class Hippocrates:
def __init__(self, location):
self.codes = {}
file = open(location, "r")
data = file.readline()
while data:
current = data.split()
x = current.pop(0)
current = ' '.join(current)
self.codes[x] = current
data = file.readline()
file.close()
def description(self, code):
if code in self.codes:
return self.codes[code]
else:
raise ValueError('invalid ICD-code')

if you use dict[key]
then it will raise a KeyError if the key does not exist.
if you use dict.get(key) then it will return None if the key does not exist.
class Hippocrates:
def __init__(self, location):
self.file = open(location, "r")
def description(self, code):
answer = {}
data = self.file.readline()
while data:
current = data.split()
x = current.pop(0)
current = ' '.join(current)
answer[x] = answer.get(current, current)
data = self.file.readline()
try:
return answer[code]
except KeyError:
print('invalid ICD-code')

Related

Calling up a return value from another module

I have 'main.py' and 'newcanmonitor.py'
I want to call up the return value "string" from newcanmonitor.py inside main.py
-----main.py----
import newcanmonitor
xy=newcanmonitor.read_bus('vcan0')
print(xy)
-----newcanmonitor.py------
def read_bus(bus_device):
"""Read data from `bus_device` until the next newline character."""
message = bus.recv(0.2)
while True:
if message:
break
message = bus.recv(0.2)
try:
string = "{}:ID={}:LEN={}".format("RX", message.arbitration_id, message.dlc)
for x in range(message.dlc):
string += ":{:02x}".format(message.data[x])
except Exception as e:
print(e)
return string
Obviously this is not working
Can you help me?
There is no reason that calling a function from another module wouldn't work.
You never use "bus_device" in your code. Could that be an omission and the source of your woes?

python function to try/except assigning variables

I want to assign variables like this:
Title = 'Lord of the Rings'
The issue here is that the value isn't always available, sometimes the server doesn't respond well and I get error that crashes my code.
And I have a lot of variables to assign.
So I decided to create a function that uses try and except:
def try_this(identificator, value):
try:
identificator = value
print(identificator,': ',value)
except:
traceback.print_exc
for item in items:
try_this(ebay_Title, item.title)
try_this(ebay_Price, item.currentPrice.value)
#etc...
Expected behavior:
ebay_title : 'Lord of the rings'
Actual behavior:
'Lord of the rings' : 'Lord of the rings'
Any errors in your code will occur at the point when you try to read item.title or item.currentPrice.value. So your proposed solution will still crash just before calling try_this.
When you call a function, Python first computes the values of the arguments, then assigns those values to variables in the function. So Python will try to read item.title or item.currentPrice.value before it calls try_this, then it will load try_this and assign the value it got to the local value variable inside the function. After that, there is no possibility of an error: you can always assign value to another variable.
So you need to wrap the reference to item.title or item.currentPrice.value in a try/except block.
This is the most straightforward way to do that:
try:
ebay_Title = item.title
except:
# handle error here, e.g., assign a default value
traceback.print_exc()
try:
ebay_Price = item.currentPrice.value
except:
# handle error here, e.g., assign a default value
traceback.print_exc()
As you've seen, this can get tedious. In your case, the error always occurs when you try to read an attribute, so you could write code specifically to handle that:
def get_attr(obj, attr_name, default_value=None):
try:
return getattr(obj, attr_name)
except:
# handle any errors here
traceback.print_exc()
return default_value
ebay_Title = get_attr(item, 'title')
# handle missing value (optional)
if ebay_Title is None:
...
ebay_Price = get_attr(get_attr(item, 'currentPrice'), 'value')
# handle missing value (optional)
if ebay_Price is None:
...
Note that get_attr is very similar to the built-in getattr function, but it will catch any kind of error that occurs when trying to read the attribute, not just missing-attribute errors.
If you don't like the get_attr(get_attr(item, 'currentPrice'), 'value') syntax, you could have get_attr accept a dot-separated attribute list like this:
def get_attr(obj, attr_name, default_value=None):
try:
val = obj
for attr in attr_name.split('.'):
val = getattr(val, attr)
return val
except:
# handle any errors here
traceback.print_exc()
return default_value
ebay_Title = get_attr(item, 'title')
ebay_Price = get_attr(item, 'currentPrice.value')
As said before, you really don't want to do this but here is how you can.
def try_this(identificator, value):
try:
globals()[identificator] = value
print(identificator,': ',value)
except:
traceback.print_exc
for item in items:
try_this("ebay_Title", item.title)
try_this("ebay_Price", item.currentPrice.value)
It would be better to use a predeclared object like:
itemDict = {
"ebay_Title": "",
"ebay_Price": ""
}
def try_this(identificator, value):
try:
itemDict[identificator] = value
print(identificator,': ',value)
except:
traceback.print_exc
for item in items:
try_this("ebay_Title", item.title)
try_this("ebay_Price", item.currentPrice.value)
In the comments thread, the OP stated:
It's a server side error, the actual function I use is a bit more complex: for item in response.reply.searchResult.item: And sometimes the server says there's not searchResult in response.reply The the code crushes – V-cash 28 mins ago
To deal with searchResult not existing, I would do this in that portion:
for item in getattr(response.reply, 'searchResult', []):
...
If searchResult does not exist in response.reply, it will return an empty list, preventing an error and making the loop do nothing.

Returning string in place of None without if statement

Is it possible to achieve the same outcome as the code below without making a temp variable and not using a if statement? I have tried multiple things but I am quite new to python.
def get_value(dictionary, key):
temp = dictionary.get(key)
if temp = None:
return "Sorry this item doesn't exist"
else:
return temp
i.e. of what I am trying to achieve
def get_value(dictionary, key):
return dictionary.get(key) else return "Sorry this item doesn't exist"
You can specify a default value when calling get() on a dictionary. Try this:
def get_value(dictionary, key):
default = "Sorry this item doesn't exist"
return dictionary.get(key, default)
Returning a string instead of None is something of an anti-pattern, since there's no way to distinguish between "Sorry this item doesn't exist" as an error message or as the actual value associated with the given key. Raise an exception instead.
def get_value(dictionary, key):
try:
return dictionary[key]
except KeyError:
raise Exception("Sorry, this item doesn't exist")
You can define your own custom exception in place of Exception. Also consider whether your exception really adds anything of value over the KeyError already raised by dict.__getitem__.

How to raise error if user tries to enter duplicate entries in a set in Python?

I tried to enter 5 similar elements in a set and print it at last. It took all the elements without raising any error but stored only one since all were same. I want to know if it is possible that the moment i input a value which is already present in the set , user is prompted with an error that the value already present in the set. I want to do this with set only , not with list or dict on anything else.
If you want to raise an exception on duplicate insertion, you could do something like this:
class DuplicateKeyError(Exception): pass
class SingleSet(set):
def add(self, value):
if value in self:
raise DuplicateKeyError('Value {!r} already present'.format(value))
super().add(value)
def update(self, values):
error_values = []
for value in values:
if value in self:
error_values.append(value)
if error_values:
raise DuplicateKeyError('Value(s) {!r} already present'.format(
error_values))
super().update(values)
my_set = SingleSet()
value = 'something'
while value:
value = input('Enter a value: ')
try:
my_set.add(value)
except DuplicateKeyError as e:
print(e)
You can just check if it exist, then raise an error:
my_set = set()
for i in some_list_of_user_input:
if i in my_set:
print('{} is already present in the set'.format(i))
my_set.add(i)

Python - Continuing after exception at the point of exception

I'm trying to extract data from an xml file. A sample of my code is as follows:
from xml.dom import minidom
dom = minidom.parse("algorithms.xml")
...
parameter = dom.getElementsByTagName("Parameters")[0]
# loop over parameters
try:
while True:
parameter_id = parameter.getElementsByTagName("Parameter")[m].getAttribute("Id")
parameter_name = parameter.getElementsByTagName("Name")[m].lastChild.data
...
parameter_default = parameter.getElementsByTagName("Default")[m].lastChild.data
print parameter_id
print parameter_default
m = m+1
except IndexError:
#reached end of available parameters
pass
#except AttributeError:
#parameter doesn't exist
#?
If all elements for each parameter exist, the code runs correctly. Unfortunately the data I am supplied often has missing entries in it, raising an AttributeError exception. If I simply pass on that error, then any elements that do exist but are retrieved later in the loop than when the exception occurred are skipped, which I don't want. I need some way to continue where the code left off and skip to the next line of code if this specific exception is raised.
The only way to work around this that I can think of would be to override the minidom's class methods and catch the exception there, but that seems far too messy and too much work to handle what should be a very simple and common problem. Is there some easier way to handle this that I am missing?
Instead of "an individual try-except block for every statement", why not abstract out that part?
def getParam(p, tagName, index, post=None):
post = post or lambda i: i
try:
return post(p.getElementsByTagName(tagname)[index])
except AttributeError:
print "informative message"
return None # will happen anyway, but why not be explicit?
then in the loop you could have things like:
parameter_id = getParam(parameter, "Parameter", m, lambda x: x.getAttribute("Id"))
parameter_name = getParam(parameter, "Name", m, lambda x: x.lastChild.data)
...
I think there are two parts to your question. First, you want the loop to continue after the first AttributeError. This you do by moving the try and except into the loop.
Something like this:
try:
while True:
try:
parameter_id = parameter.getElementsByTagName("Parameter")[m].getAttribute("Id")
parameter_name = parameter.getElementsByTagName("Name")[m].lastChild.data
...
parameter_default = parameter.getElementsByTagName("Default")[m].lastChild.data
print parameter_id
print parameter_default
m = m+1
except AttributeError:
print "parameter doesn't exist"
#?
except IndexError:
#reached end of available parameters
pass
The second part is more tricky. But it is nicely solved by the other answer.

Categories

Resources