Chapter 34: Exception Coding Det
The try/except/else Statement
# try/except/else
try:
print('doing sth')
except name1:
print('doing sth')
except (name1, name2):
print('doing sth')
except name4 as var:
print('doing sth')
except:
print('doing sth')
else:
print('doing sth if no exception is raised.')
How try Statements Work
- First run try blocks, the statements under try block.
- If an exception raises and is caught, run the corresponding exception block, after that, control then resumes below the entire try statement.
- If an exception is raised and not be caught, Python kills the program and prints a default error message.
- If an exception does not occur, run statements under the else line (if present).
- The empty exception clause matches all or other errors.
In sum, except clauses catch any matching exceptions that happen while the try block is running, and the else clause runs only if no exceptions happen while the try block runs.
Try Statement Clauses
Table 34-1. try statement clause forms
Clause form | Interpretation |
---|---|
except: | Catch all (or all other) exception types. |
except name: | Catch a specific exception only. |
except name as value: | Catch the exception and assign its instance. |
except (name1, name2): | Catch any of the listed exceptions. |
except (name1, name2) as value: | Catch the listed exception and assign its instance. |
else: | Run if no exceptions are raised in the try block. |
finally: | Always perform this block on exit. |
- empty except clause catch all exceptions including the ones triggered by system, such as exit calls and Ctrl-C key combinations. So it is recommended to use the following statements to avoid catching system errors:
# Exception class will catch exceptions except catching exit events.
try:
action()
except Exception:
...
The try else Clause
# provides syntax in a try that makes what has happened obvious and unambiguous.
try:
...run code...
except IndexError:
...handle exception...
else:
...no exception occurred...
Example: default behavior:
def gobad(x, y):
return x / y
def go_south(x):
print(gobad(x, 0))
go_south(1)
>>>
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
<ipython-input-1-fef879750521> in <module>
5 print(gobad(x, 0))
6
----> 7 go_south(1)
<ipython-input-1-fef879750521> in go_south(x)
3
4 def go_south(x):
----> 5 print(gobad(x, 0))
6
7 go_south(1)
<ipython-input-1-fef879750521> in gobad(x, y)
1 def gobad(x, y):
----> 2 return x / y
3
4 def go_south(x):
5 print(gobad(x, 0))
ZeroDivisionError: division by zero
Often, this standard error message is all you need to resolve problems in your code.
Catching Built-in Exceptions
If you don't want your program terminated when Python raises an exception, simply catch it by wrapping the program logic in a try.
def kaboom(x, y):
print(x + y)
try:
kaboom([0, 1, 2], 'spam')
except TypeError:
print('Hello World.')
print('Resuming here.')
>>>
Hello World.
Resuming here.
The try/finally Statement
The try/finally form is useful when you want to be completely sure that an action will happen after some code runs, regardless of the exception behavior of the program. (Such as close an opened file.)
- The finally clause could not be mixed with except and else.
- The formula of using combined try/except/else/finally:
The raise Statement
- raise is used to trigger exceptions explicitly.
raise instance # Raise instance of class (The most common)
raise class # Make and raise instance of class: make an instance
raise # Reraise the most recent exception
The following two forms are equivalent----both raise an instance of the exception class named.
raise IndexError
raise IndexError()
- create the instance ahead of time
exc = IndexError()
raise exc
excs = [IndexError, TypeError]
raise excs[0]
Pass to the exception class constructor arguments that become available in the handler through the assigned instance.
class MyExc(Exception): pass
...
raise MyExc('spam') # Exception class with constructor args
...
try:
...
except MyExc as X: # Instance attributes available in handler.
print(X.args)
Scopes and try except Variables
In Python 3.x, the exception reference name is not available after the block exits.
try:
1 / 0
except Exception as X:
print(X)
>>>
division by zero
print(X)
>>>
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-4-5f1555447371> in <module>
----> 1 print(X)
NameError: name 'X' is not defined
- The variable used in except block is removed globally even if it is defined ahead of the exception block.
X = 99
try:
1 / 0
except Exception as X:
print(X)
>>>
division by zero
print(X)
>>>
print(X)
1
print(X)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-6-5f1555447371> in <module>
----> 1 print(X)
NameError: name 'X' is not defined
- This is unlike what happens in compression loop variables.
X = 99
>>>
{'a', 'm', 'p', 's'}
print(X)
>>>
99
Because of this, your should generally use unique variable names in your try statement's except clause.
- You can save the exception if needed.
try:
1 / 0
except Exception as X:
print(X)
save_it = X
>>>
division by zero
X
>>>
NameError: name 'X' is not defined
save_it
ZeroDivisionError('division by zero')
Python 3.X Exception Chaining: raise from
raise newexception from otherexception
The assert Statement
The assert is mostly just syntactic shorthand for a common raise usage pattern.
assert test, data # The data part is optional
works like the following code:
if __debug__:
if not test:
raise AssertionError(data)
If the test evaluates to false, Python raises an exception: the data item (if it's provided) is used as the exception's constructor argument. Like all exceptions, the AssertionError exception will kill your program if it's not caught with a try.
Example
def f(x):
assert x < 0, 'x must be negative'
return x ** 2
f(1)
>>>
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-12-3a4297a3995e> in <module>
3 return x ** 2
4
----> 5 f(1)
<ipython-input-12-3a4297a3995e> in f(x)
1 def f(x):
----> 2 assert x < 0, 'x must be negative'
3 return x ** 2
4
5 f(1)
AssertionError: x must be negative
with/as Context Managers
The with/as statement is designed to be an alternative to a common try/finally usage idiom.
Basic Usage
with expression [as variable]:
with-block
The expression here is assumed to return an object that supports the context management protocol. (Usually file). It closes the file like finally.
- The object returned by the expression may then run startup code before the with-block is started, as well as termination code after the block is done, regardless of whether the block raised an exception or not.
- two examples
operate a file:
with open(r'c:\misc\data') as myfile:
for line in myfile:
print(line)
more code...
- thread lock
# The context management machinery guarantees that the lock is automatically acquired before the block is executed and released once the block is complete.
lock = threading.Lock()
with lock:
...access shared resources...
- set decimal precision
with decimal.localcontext() as ctx:
ctx.prec = 2
x = decimal.Decimal('1.00') / decimal.Decimal('3.00')
After this statement runs, the current thread's context manager state is automatically restored to what it was before the statement began.
The Context Management Protocol
- How the with statement actually works:
- The expression is evaluated, resulting in an object known as a context manager that must have enter and exit methods.
- The context manager's enter method is called. The value it returns is assigned to the variable in the as clause if present, or simply discarded otherwise.
- No matter whether the with block raises an exception, the exit will be called. The exception, if is raised, will assign the details in the type, value, traceback in the exit. These variables will be assigned to None otherwise.
Multiple Context Managers in 3.1, 2.7, and later
...pass...