一、异常
即使语法或者表达式在语法上是正确的,但在尝试执行时,它仍可能会引发错误。在执行时检测到的错误被称为异常。
二、处理异常
while True:
try:
x = int(input('Please enter a number: '))
break
except ValueError:
print('Oops! That was no valid number. Try again ...')
try
语句的工作原理如下:
- 首先,执行
try
语句(try
和except
关键字之间的(多行)语句) - 如果没有异常发生,则跳过
except
语句,并完成try
语句的执行 - 如果在执行
try
子句时发生了异常,则跳过该子句的剩下部分,然后,如果异样的类型和except
关键字后面的异常匹配,则执行except
子句,然后继续执行try
语句之后的代码 - 如果发生的异常和
except
子句中指定的异常不匹配,则将其传递给外部的try
语句中,如果没有找到处理程序,则它是一个 未处理异常,执行将停止并显式异常信息。
一个 try
语句可能由多个 except
子句,以指定不同异常的处理程序。最多会执行一个处理程序。处理程序只处理相应的 try
子句中发生的异常,而不处理同一 try
语句内其他处理程序中的异常。 一个 except
子句可以将多个异常名为带括号的元组,例如
except (RuntimeError, TypeError, NameError):
pass
如果发生的异常和 except
子句中的类是同一个类或者是它(指这个发生的异常)的基类,则异常和 except
子句中的类是兼容的(但反过来不成立——列出派生类的 except
子句与基类不兼容)
class B(Exception):
pass
class C(B):
pass
class D(C):
pass
# 例一:
for cls in [B, C, D]:
try:
raise cls()
except B:
print('B')
except C:
print('C')
except D:
print('D')
# 例二:
for cls in [B, C, D]:
try:
raise cls()
except D:
print('D')
except C:
print('C')
except B:
print('B')
例一中打印的结果为B,B,B
例二中打印的结果为 B, C ,D
首先看例一:
对于cls 等于 B时,首先捕获第一个异常,且与B是同一个类,所以打印出B;对于cls 等于 C时,这个异常是第一个except——B的子类,也就是cls——C是B的基类,由于如果发生的异常和 except
子句中的类是同一个类或者是它(指这个发生的异常)的基类,则异常和 except
子句中的类是兼容的所以就会触发第一个异常,打印出B,同理对于cls等于D也是。
所以对于例二:
由例一以及列出派生类的 except
子句与基类不兼容。
最后的 except
子句可以省略异常名,以用作通配符。但谨慎使用,因为这种方式很容易掩盖真正的编程错误。
import sys
try:
f = open(r"C:\Users\David Wolfowitz\Desktop\test.txt", 'r')
s = f.readline()
i = int(s.lstrip())
except OSError as err:
print("OS error : {0}".format(err))
except ValueError:
print("Could not convert data to an integer.")
except:
print("Unexcepted error: ", sys.exc_info()[0])
raise
try
…except
语句由一个可选的 else
子句,在使用时必须放在所有的 except
子句后面。 对于在 try
子句不引发异常时必须执行的代码来说很有用。例如:
import sys
for arg in sys.argv[1:]:
try:
f = open(arg, 'r')
except OSError:
print('can not open', arg)
else:
print(arg, 'has', len(f.readlines()), 'lines')
f.close()
使用else
子句比向try
子句添加额外的代码要好,因为它避免了意外捕获由 try
…except
语句保护的代码未引发的异常。
发生异常时,它可能具有关联值,也称为异常参数。参数的存在和类型取决于异常类型。
except
子句可以在异常名称后面指定一个变量。一个变量和一个异常实例绑定,它的参数存储在 instance.args 中。为方便起见,异常实例定义了 str(), 因此可以直接打印参数而无需引用 .args。
try:
raise Exception('spam', 'egg')
except Exception as inst:
print(type(inst))
print(inst)
print(inst.args)
print(inst.__str__())
x, y = inst.args
print(x)
print(y)
异常处理程序不仅处理try
子句子句中遇到的异常,还处理try
子句中调用(即使是间接的)的函数内部发生的异常。
例如:
def this_fails():
x = 1/0
try:
this_fails()
except ZeroDivisionError as err:
print('Handing run-time error: ', err)
三、抛出异常
raise
语句运行程序员强制发生指定的异常, raise
的唯一参数就是要抛出的异常,这个参数必须是一个异常实例或者异常类(派生自 Eexception的类),如果传递的是一个异常类,它将通过调用没有参数的构造函数来隐式实例化
try:
raise NameError('HiThree')
except NameError:
print('An excpetion flew by')
raise
四、用户自定义异常
程序可以通过创建新的异常类来命名它们自己的异常。异常通常应该直接或间接地从 Exception 类派生。
class Error(Exception):
"""Base class for exceptions in this module."""
pass
class InputError(Error):
"""Exception raised for errors in the input.
Attributes:
expression -- input expression in which the error occurred
message -- explanation of the error
"""
def __init__(self, expression, message):
self.expression = expression
self.message = message
class TransitionError(Error):
"""Raised when an operation attempts a state transition that's not
allowed.
Attributes:
previous -- state at beginning of transition
next -- attempted new state
message -- explanation of why the specific transition is not allowed
"""
def __init__(self, previous, next, message):
self.previous = previous
self.next = next
self.message = message
五、定义清理操作
try
语句有另一个可选自子句,用户定义必须在所有地情况下执行的操作。例如:
try:
raise KeyboardInterrupt
finally:
print("GoodBye, world!")
如果存在 finally
子句,则 finally
子句将作为 try
语句结束前的最后一项任务被执行。finally
子句不论try
语句是否产生了异常都会被执行
- 如果在执行
try
子句期间发生了异常,该异常可由一个except
子句进行处理。 如果异常没有被某个except
子句所处理,则该异常会在finally
子句执行之后被重新引发。 - 异常也可能在
except
或else
子句执行期间发生。 同样地,该异常会在finally
子句执行之后被重新引发。 - 如果在执行
try
语句时遇到一个break
,continue
或return
语句,则finally
子句将在执行break
,continue
或return
语句之前被执行。 - 如果
finally
子句中包含一个return
语句,则返回值将来自finally
子句的某个return
语句的返回值,而非来自try
子句的return
语句的返回值。
def bool_return():
try:
return True
finally:
return False
print(bool_return())
def division(x, y):
try:
result = x / y
except ZeroDivisionError:
print('division by zero!')
else:
print("result is: ", result)
finally:
print("executiong finally clause")
print(division(2, 1))
print('*' * 20)
print(division(2, 0))
print('*' * 20)
print(division('2', '1'))
结果:
finally 子句在任何情况下都会被执行。 两个字符串相除所引发的 TypeError 不会由 except 子句处理,因此会在 finally 子句执行后被重新引发。
六、预定义的清理操作
使用with
语句允许像文件这样的对象能够以一种确保它们得到及时和正确的清理的方式使用。:
例如:
with open("myfile.txt") as f:
for line in f:
print(line, end="")
参考文献:
【1】 错误和异常