I am having trouble understanding the following control flow.
try:
# normal execution block
except A:
# handle exception A
except B:
# handle exception B
except:
# handle other exceptions
else:
# if no exceptions, go here
finally:
# always do this
I don't understand the purpose of the a else in this context. I am coming from Java where there is no else clause for handling exceptions.
If I have something to write in the else part, I would assume that I can directly write it outside of the exception handling part either.
So, whats the necessity of the else clause in Python exception handling?
If I have something to write in else clause,I can directly write outside of the exception handle part either.
No.
def with_else(x):
try:
int(x)
except ValueError:
print('that did not work')
else:
print('that worked!')
def without_else(x):
try:
int(x)
except ValueError:
print('that did not work')
print('that worked!')
Demo:
>>> with_else(1)
that worked!
>>> without_else(1)
that worked!
>>> with_else('foo')
that did not work
>>> without_else('foo')
that did not work
that worked!
processing = True
try:
x = might_raise_a_key_error()
# a
except KeyError:
...
else:
# b
finally:
processing = False
# c
If you have a piece of code which 1) depends on x, 2) you don’t want handled by except KeyError, but 3) you do want covered by the finally clause, do you put it in # a, # b or # c?
Answer: # b.
Related
I only want my list to have one element in it. There should be one exception raised if the list is empty and a different exception raised if the list has more than one element.
The following code accomplishes the task, but I don't think it's written as well as it should by any standards out there. Additionally, if the condition is met that the list is greater than one, I want it to print the python error "index out of range" as it does in the other condition. How can I improve upon this?
x = []
try:
if len(x) > 1:
raise Exception
else:
testVar = x[0]
except IndexError as e:
print(e)
print("list does not have any elements.")
except Exception as e:
print(e)
print("There are too many elements in the list.")
Here is a better way to write that.
def func(x):
if not x:
print("list does not have any elements.")
return None
if len(x) > 1:
print("There are too many elements in the list.")
return None
return x[0]
Note that if we omit the first three lines, then Python will automatically raise an IndexError when you refer to x[0].
I'm parsing an XML file using Beautiful Soup. Sometimes I have entries that are missing one or more of the keys I'm parsing. I want to setup exceptions to handle this. My code looks something like this:
for entry in soup.findAll('entry_name'):
try:
entry_dict = dict(entry.attrs)
x = entry_dict["x"]
y = entry_dict["y"]
z = entry_dict["z"]
d[x] = [y, z]
except KeyError:
y = "0"
d[x] = [y, z]
The problem is I can have "y", "z" or both "y and z" missing depending on the entry. Is there a way to handle specific KeyErrors? Something like this:
except KeyError "y":
except KeyError "z":
except KeyError "y","z":
You can check for exception arguments:
a = {}
try:
a['a']
except KeyError as e:
# handle key errors you want
if e.args[0] == 'a':
pass
# reraise the exception if not handled
else:
raise
Personally I wouldn't use a try/except here and instead go for the detection approach instead of the handling approach.
if not 'y' in entry_dict.keys() and not 'z' in entry_dict.keys():
# handle y and z missing
elif not 'y' in entry_dict.keys():
# handle missing y
elif not 'z' in entry_dict.keys():
# handle missing z
A slight variation on Paweł Kordowski answer:
a = {}
try:
a['a']
except KeyError as e:
# 2 other ways to check what the missing key was
if 'a' in e.args:
pass
if 'a' in e:
pass
# reraise the exception if not handled
else:
raise
I have built a max heap and trying to extract max as long as there are elements. If there isn't I'm returning an IndexError. This the code I'm trying to execute:
while True:
try:
print hp.extract_max()
except:
break
and in the extract_max() method:
def extract_max(self):
if self.size == 0:
return IndexError
item = self.items[0]
self.items[0] = self.items[self.size - 1]
self.heapify_down()
del self.items[len(self.items) - 1]
return item
However, the code is not breaking upon encountering an IndexError, rather printing it too. The while loop is not breaking.
<type 'exceptions.IndexError'>
<type 'exceptions.IndexError'>
....
it keeps printing the exception, without breaking the loop.
What's the problem?
As the other answer stated, you should raise the exception instead of return so:
if self.size == 0:
raise IndexError
I just want to add that you are catching all type of exceptions with except, you might want to change it to catch IndexError only to avoid catching other exceptions (ex: KeyboardInterrupt) as the following:
while True:
try:
print hp.extract_max()
except IndexError:
break
You should be raising an exception, not returning it. Do raise IndexError.
Current situation:
def isTooLarge(intValue):
if intValue > 100: print "too large"; return True
return False
if isTooLarge(101): break
Now I like to make the function more "library-friendly" by returning the errormessagetext instead of printing it:
def isTooLarge(intValue):
if intValue > 100: return True, "too large"
return False
bln,str = isTooLarge(101)
if bln: specialprint(str); break
Any idea how I can evaluate it as an one-liner again? (something like "if ,str isTooLarge(101): specialprint(str); break" or what is the Python way here?
No problem to put the errormessage into a global variable like "lasterrormessage" and keep the rest as is.
You can be much more "library friendly" by using errors like they are meant for, as errors:
def CheckNotTooLarge(intValue):
if intValue > 100:
raise ValueError("too large") #or AssertionError
return #or maybe do something else?
then a user could use the error message completely separately using try: except:
try:
CheckNotTooLarge(101)
except ValueError:
traceback.print_exc() #print error message
#handle too large
else:
#handle not too large
I can see how this would quickly get annoying if you just want to check without handling errors so I'd recommend having two functions, one that just returns a boolean, no extra work and another that raises/returns the error text:
def isTooLarge(intValue):
return intValue<=100 #now this is a one liner!
def checkIsTooLarge(intValue):
"uses isTooLarge to return an error text if the number is too large"
if isTooLarge(intValue):
return "too large" #or raise ...
else:
return False
def isTooLarge(intValue):
return "too large" if intValue > 100 else False
x = isTooLarge(101); x and print(x)
However. Please don't do this. Exceptions exist for a reason. Putting things in one line simply for the sake of it makes your code hard to read.
def FancyDivide(numbers, index):
try:
try:
denom = numbers[index]
for i in range(len(numbers)):
numbers[i] /= denom
except IndexError, e:
FancyDivide(numbers, len(numbers) - 1)
else:
print "1"
finally:
print "0"
except ZeroDivisionError, e:
print "-2"
Function Output
>>> FancyDivide([0, 2, 4], 0)
0
-2
I expect only -2 as the answer but why 0. According to me ZeroDivisionError should be caught by last except statement and therefore the answer should be -2. Confused. Please Help.
The finally block will execute regardless of if an Exception is thrown or not.
With your code throwing the exception inside the nested try, the associated finally block will execute before the outer except.
You're right, the ZeroDivisionError will be caught by the outer try block, but finally clauses are always executed before leaving their respective try block (even when an exception is raised within it).
So in your case, the inner try raises the exception, the inner finally is executed, then the outer try catches the exception.
More info here: https://docs.python.org/2/tutorial/errors.html#defining-clean-up-actions