Understanding Exception Handling in Python - python

I have two questions on exception handling.
Q1) I am slightly unsure as to when exactly the operations within else will be carried out in exception handling. I am unsure when the else block would be executed, which doesn't occur in the code below:
def attempt_float(SecPrice,diffprice):
try:
return float(SecPrice)
except:
return diffprice
else:
print "Did we succeed?"
print attempt_float('7','3')
Q2) When I run the code below:
def attempt_float(SecPrice,diffprice):
try:
return float(SecPrice)
except:
return diffprice
else:
print "Did we succeed?"
finally:
print "Yasdsdsa"
print attempt_float('7','3')
I am unclear as to why the output is:
Yasdsdsa
7.0

When Python encounters a return-statement inside a function, it immediately returns (exits) from the function. This means that when you do:
try:
return float(SecPrice)
...
else:
print "Did we succeed?"
"Did we succeed?" will never be printed because you returned in the try: block, thereby skipping the execution of the else: block.
Your second code piece is different however because you used a finally: block. Code inside a finally: block is always executed, no matter if an exception is raised, you return from a function, etc. This is to ensure that any cleanup code which is important (i.e. frees resources) is always executed and not accidentally skipped.
You can read about this behavior in the docs both here:
When return passes control out of a try statement with a finally
clause, that finally clause is executed before really leaving the
function.
as well as here:
When a return, break or continue statement is executed in the try
suite of a try...finally statement, the finally clause is also
executed "on the way out."
As for why the output is:
Yasdsdsa
7.0
and not:
7.0
Yasdsdsa
the answer is that the print "Yasdsdsa" line is executed in the finally: block before Python is able to print 7.0 (the return value of attempt_float). Put simply, the execution path for Python is:
Return float(SecPrice).
Run the finally: block.
Resume normal execution with the print attempt_float('7','3') line and print 7.0.

In the first case, you return within try, so you never hit the else statement.
In the second one, the finally is executed regardless of how try is exited. From the python docs:
A finally clause is always executed before leaving the try statement, whether an exception has occurred or not. (...) The finally clause is also executed “on the way out” when any other clause of the try statement is left via a break, continue or return statement.
Here is a good example of the order of execution:
>>> def divide(x, y):
... try:
... result = x / y
... except ZeroDivisionError:
... print "division by zero!"
... else:
... print "result is", result
... finally:
... print "executing finally clause"
...
>>> divide(2, 1)
result is 2
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Be sure to read the docs on exceptions!

Related

Problem with nonexistent return statement in ZeroDivisionError in Python

i've been doing the "Automate the Boring Stuff with Python Programming" course, and I don't understand why author says that the reason of None value being in output is a fact, that there is no return statement in function below. I I thought that "return 42/divideBy" is a return statement that is needed. I am very much new in the world of programming, so i would appreciate an easy explanation. Thanks
def div42by (divideBy):
try:
return 42/divideBy
except ZeroDivisionError:
print ('Error: You tried to divide by zero.')
print (div42by(2))
print (div42by(12))
print (div42by(0))
print (div42by(1))
(output)
21.0
3.5
Error: You tried to divide by zero.
None
42.0
You are correct that return 42/divideBy is a return statement.
But when divideBy is equal to zero, the 42/divideBy part will raise a ZeroDivisionError, which means the return never actually runs. Instead, Python jumps to the print ('Error: You tried to divide by zero.') line and continues running from there. There's no return statement after that line, so Python automatically returns None instead.
Another way to look at it is that your div42by function is equivalent to this:
def div42by(divideBy):
try:
result = 42 / divideBy
return result
except ZeroDivisionError:
print('Error: You tried to divide by zero.')
And the return result line never runs when divideBy is zero.
The return statement is not executed because dividing by zero raises an ZeroDivisionError.
This ZeroDivisionError is then caught by the try->except block.
The order in which things happen is:
python3 tries to execute 42 / divideBy (which is zero)
42 / divideBy raises the ZeroDivisionError
the ZeroDivisionError is caught by the try-except
the except-block is executed, making python3 print the error message
the function continues, but there is no Code left
No return statement gets executed
To still get a return, there'd need to be a return statement below try->except but still inside the function.

How do we write an `except` statement which catches nothing?

I want a try-block such that any exception raised inside of thetry-block goes unhandled. This is so that I can write a try block in preparation for the future. Some day, I will write in some meaningful error handling. However, I don't have real except statements yet. The following sort of works, but is ugly
_ = type("", (Exception,), dict())
try:
lizard = [1, 2, 3]
y = z + w
print(lizard[983])
except _:
print("I hope this string never prints")
Skip the except clause altogether. A try statement needs at least one except clause or a finally clause, which executes whether or not you catch an exception.
try:
lizard = [1, 2, 3]
y = z + w
print(lizard[983])
finally:
pass
The finally clause won't actually execute any code, and does not affect the control flow of your code in any way; it just injects a no-op before you leave the try statement, whether by successfully completing the code or by raising an uncaught exception.
Once you start adding except clauses, you can either remove the finally clause or leave it in place.
(A deleted answer catches and immediately reraises any exception, which is also fine IMO:
try:
...
except Exception:
raise
)
try:
# do something
except:
pass # this will make nothing
the pass keyword is used for that purpose. when you want to make nothing, just write some code and come back later and think about what you really want to do there (or at least that's how I use it)

python: try/except/else and continue statement

Why is the output of the below python code snippet NOT just No exception:1, since during first iteration there is no exception raised. From python docs (https://docs.python.org/2.7/tutorial/errors.html).
The try ... except statement has an optional else clause, which, when
present, must follow all except clauses. It is useful for code that
must be executed if the try clause does not raise an exception.
$ cat hello.py
for x in range(1,10):
try:
if x == 1:
continue
x/0
except Exception:
print "Kaput:%s" %(x)
else:
print "No exception:%s" %(x)
break
$ python hello.py
Kaput:2
Kaput:3
Kaput:4
Kaput:5
Kaput:6
Kaput:7
Kaput:8
Kaput:9
$ python -V
Python 2.7.8
The tutorial gives a good start, but is not the language reference. Read the reference here.
Note in particular:
The optional else clause is executed if and when control flows off the end of the try clause.
clarified by footnote 2:
Currently, control “flows off the end” except in the case of an exception or the execution of a return, continue, or break statement.
So your use of continue is explicitly addressed by that.
Your code has a continue, so it never gets to the else block. To achieve your result, you can not get to the continue:
Code:
for x in range(1, 10):
try:
if x != 1:
x / 0
except Exception:
print "Kaput:%s" % (x)
else:
print "No exception:%s" % (x)
break
Result:
No exception:1
It has to do with your use of continue and break. I think this is the functionality you're going for. Basically, continue does not skip to the else statement, it continues on with the code (passed the try statement). And, break breaks the for loop, thus producing no more output, so I removed that statement.
for x in range(1,10):
try:
if x != 1:
x/0
except Exception:
print "Kaput:%s" %(x)
else:
print "No exception:%s" %(x)
This is because of the continue statement... its taking the control to for statement.. try removing the continue and add a conditional statement for x/0, like if(x!=1): x/0 then see the output you desire..

Why does `finally: return 42` clear an uncaught exception? [duplicate]

This is some code that is behaving peculiarly. This is a simplified version of the behavior that I've written. This will still demonstrate the weird behavior and I had some specific questions on why this is occurring.
I'm using Python 2.6.6 on Windows 7.
def demo1():
try:
raise RuntimeError,"To Force Issue"
except:
return 1
else:
return 2
finally:
return 3
def demo2():
try:
try:
raise RuntimeError,"To Force Issue"
except:
return 1
else:
return 2
finally:
return 3
except:
print 4
else:
print 5
finally:
print 6
Results:
>>> print demo1()
3
>>> print demo2()
6
3
Why is demo one returning 3 instead of 1?
Why is demo two printing 6 instead of printing 6 w/ 4 or 5?
Because finally statements are guaranteed to be executed (well, presuming no power outage or anything outside of Python's control). This means that before the function can return, it must run the finally block, which returns a different value.
The Python docs state:
When a return, break or continue statement is executed in the try suite of a try…finally statement, the finally clause is also executed ‘on the way out.’
The return value of a function is determined by the last return statement executed. Since the finally clause always executes, a return statement executed in the finally clause will always be the last one executed:
This means that when you try to return, the finally block is called, returning it's value, rather than the one that you would have had.
The execution order is:
try block all completes normally -> finally block -> function ends
try block run and get into exception A -> finally block -> function ends
try block make a return value and call return -> finally block -> popup return value -> function ends
So, any return in the finally block will end the steps in advance.
Note that there has been PEP601 to forbid return statements from finally clauses, but it has been rejected.
It has however been added to the style guide in PEP8 that it should be avoided.

Understanding python try catch else finally clause behavior

Python 2.6.5 (r265:79063, Oct 1 2012, 22:07:21)
I have this:
def f():
try:
print "a"
return
except:
print "b"
else:
print "c"
finally:
print "d"
f()
This gives:
a
d
and not the expected
a
c
d
If I comment out the return, then I will get
a
c
d
How do I remember this behavior in python?
When in doubt, consult the docs:
The optional else clause is executed if and when control flows off the end of the try clause
Currently, control “flows off the end” except in the case of an exception or the execution of a return, continue, or break statement.
Since you're returning from the body of the try block, the else will not be executed.
finally blocks always happen, save for catastrophic failure of the VM. This is part of the contract of finally.
You can remember this by remembering that this is what finally does. Don't be confused by other control structures like if/elif/else/while/for/ternary/whatever statements, because they do not have this contract. finally does.
The else block hooked up to a try block is not special in terms of exception mechanics - it's similar to just putting code in the body of the function after the try block. The only function is being able execute "normal" code between a try block and a finally block. This does mean however that if the function returns before getting to the else block it will never execute.
Take a look at Python documentation!
You would use this for exception handling, bookmark this page!
http://docs.python.org/2/tutorial/errors.html
You enter the try block.
You print "a".
You encounter the return statement and the function returns.
Since finally blocks always execute, though, everything in the finally block happens before the function actually ends its execution.
The else block is only entered if the try block has been completed and no exception occurred. Because you returned in the try block, it wasn't completed, even though no exception occurred nonetheless. It is as if you never had a try/else block at all, and you put all the code on the same level of indentation.
try/else exists more for the sake of code organization; unless a finally block is added, it is the same thing as using no else block at all. The following are functionally equivalent:
try:
print "foo"
except:
print "woops"
else:
print "bar"
try:
print "foo"
except:
print "woops"
print "bar"
However, you must use else if you are going to use finally. This has no meaning:
try:
print "foo"
except:
print "woops"
print "bar"
finally:
print "done"
What was the finally supposed to refer to in this case? Ergo you must use else if you are going to use finally unless you did not intend for anything to happen after the try block but before the finally.
When a return, break or continue statement is executed in the try suite of a try…finally statement, the finally clause is also executed ‘on the way out.’ A continue statement is illegal in the finally clause. (The reason is a problem with the current implementation — this restriction may be lifted in the future).
This is actually from the documentation.

Categories