一、异常与错误
Python机制设置了异常机制。异常指的是运行时程序遇到的可以被捕捉的错误。程序捕捉了异常,而不至于让程序运行错误而crash。异常增强了程序的运行可靠性。
我们来看个例子来对比下错误和异常的区别
- 错误:
import sys
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
print("code reachs here.")
- 异常:
import sys
try:
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
except OSError as err:
print("OS error:", err)
except ValueError:
print("Could not convert data to an integer.")
except Exception as err:
print(f"Unexpected {err=}, {type(err)=}")
raise
print("code reachs here.")
我们可以看到运行了带异常捕获的例子,程序打印出了code reachs here
。
二、异常的分类
2.1 自定义异常
异常的基类Exception,一般我们继承Excpetion类来自定义异常类。
自定义异常类往往只提供一些属性保持简单,运行程序提取有关错误的信息。
大多数异常命名都以 “Error” 结尾,类似Python标准提供异常的命名。
比如说这种写法:
class AppException(Exception):
""" Application exception, will not be logged ass an error """
pass
class UnknownSSIDError(AppException):
def __init__(self, ssid):
self.ssid = ssid
super(UnknownSSIDError, self).__init__("Unknown ssid '%s'" % ssid)
2.2 内置异常分类
BaseException是所有异常的共同基类。它的一个子类Exception是所有非致命异常的基类。不是Exception的子类的异常通常不被处理。它们被用来指示程序应该终止,包括由sys.exit()引发的SystemExit,以及当用户希望中断程序时引发的 KeyboardInterrupt。
完整的Python的Excpetion见下图:
BaseException
├── BaseExceptionGroup
├── GeneratorExit
├── KeyboardInterrupt
├── SystemExit
└── Exception
├── ArithmeticError
│ ├── FloatingPointError
│ ├── OverflowError
│ └── ZeroDivisionError
├── AssertionError
├── AttributeError
├── BufferError
├── EOFError
├── ExceptionGroup [BaseExceptionGroup]
├── ImportError
│ └── ModuleNotFoundError
├── LookupError
│ ├── IndexError
│ └── KeyError
├── MemoryError
├── NameError
│ └── UnboundLocalError
├── OSError
│ ├── BlockingIOError
│ ├── ChildProcessError
│ ├── ConnectionError
│ │ ├── BrokenPipeError
│ │ ├── ConnectionAbortedError
│ │ ├── ConnectionRefusedError
│ │ └── ConnectionResetError
│ ├── FileExistsError
│ ├── FileNotFoundError
│ ├── InterruptedError
│ ├── IsADirectoryError
│ ├── NotADirectoryError
│ ├── PermissionError
│ ├── ProcessLookupError
│ └── TimeoutError
├── ReferenceError
├── RuntimeError
│ ├── NotImplementedError
│ └── RecursionError
├── StopAsyncIteration
├── StopIteration
├── SyntaxError
│ └── IndentationError
│ └── TabError
├── SystemError
├── TypeError
├── ValueError
│ └── UnicodeError
│ ├── UnicodeDecodeError
│ ├── UnicodeEncodeError
│ └── UnicodeTranslateError
└── Warning
├── BytesWarning
├── DeprecationWarning
├── EncodingWarning
├── FutureWarning
├── ImportWarning
├── PendingDeprecationWarning
├── ResourceWarning
├── RuntimeWarning
├── SyntaxWarning
├── UnicodeWarning
└── UserWarning
三、异常处理
这段代码我们看到了几个关键动作。
- 一个是raise 向外抛出了异常
- 外面用try...except...捕获了异常。
try
语句可以有多个except 子句来为不同的异常指定处理程序。 但最多只有一个处理程序会被执行。 except 子句可以用带圆括号的元组来指定多个异常。此外try除了except还可以带else。 - 捕获异常的优先级分别是各个的except从上到下去比对。且认为派生类异常会等于基类异常。所以在第一个for循环我们看到了输出,D-C-B。但是下一个for循环我们会看到输出,B-B-B。是因为第一个excpet B截获了所有的Exception。
class B(Exception):
pass
class C(B):
pass
class D(C):
pass
for cls in [B, C, D]:
try:
raise cls()
except D:
print("D")
except C:
print("C")
except B:
print("B")
for cls in [B, C, D]:
try:
raise cls()
except B:
print("B")
except C:
print("C")
except D:
print("D")
3.1 try...except...else
try
... except
... else
...finally
...,
finally
子句是可选的,它是try
语句结束前执行的最后一项任务。不论try
语句是否触发异常,都会执行到finally
子句。- else也是可选的。如果存在else,那么必须将else放在所有 except 子句 之后。 它适用于 try 子句 没有引发异常但又必须要执行的代码。 例如:
for arg in sys.argv[1:]:
try:
f = open(arg, 'r')
except OSError:
print('cannot open', arg)
else:
print(arg, 'has', len(f.readlines()), 'lines')
f.close()
异常处理程序不仅会处理在 try 子句 中立刻发生的异常,还会处理在 try 子句 中调用(包括间接调用)的函数。
处理 Exception
最常见的模式是打印或记录异常,然后重新raise(允许调用者也处理异常):
3.2 添加异常追踪栈
异常是可以添加个性化信息的,比如说下面这个例子。我们可以通过args或者直接print这个异常输出个性化信息。
try:
raise Exception('spam', 'eggs')
except Exception as inst:
print(type(inst)) # the exception type
print(inst.args) # arguments stored in .args
print(inst) # __str__ allows args to be printed directly,
# but may be overridden in exception subclasses
x, y = inst.args # unpack args
print('x =', x)
print('y =', y)
# output:
#<class 'Exception'>
#('spam', 'eggs')
#('spam', 'eggs')
#x = spam
#y = eggs
除了在Exception被创建的时候添加信息,我们还可以在Exception被处理的时候通过add_note方法追加信息。这时候我们添加这层堆栈的信息再raise出去给调用者捕获,那么这个Exception通过层层add_note就有个堆栈的全貌。
add_note(note)
方法接受一个字符串,并将其添加到异常的注释列表。标准的回溯在异常之后按照它们被添加的顺序呈现所有的注释。
try:
raise TypeError('bad type')
except Exception as e:
e.add_note('Add some information')
e.add_note('Add some more information')
raise
#Traceback (most recent call last):
# File "<stdin>", line 2, in <module>
#TypeError: bad type
#Add some information
#Add some more information