在python中,异常会根据错误自动地被触发,也能由代码触发和截获。 异常由四个语句进行处理:python
try/except: 捕捉由python或你引发的异常,并恢复。程序员
try/finally: 不管异常是否发生,执行清理行为。不管是否异常,最后都必须执行finally;web
try/except/else/finally: 即先执行try,若是有异常,执行except;若是没有异常,执行else;最后,都执行finally;数据库
raise: 抛出异常,手动在代码中触发异常。函数
assert: 断言,有条件地在程序员代码中触发异常。主要用于代码调式 或 强制程序知足某个条件,不然报错;spa
with/as: 在python2.6及后续版本中实现环境管理器。咱们在文件打开自动关闭那里就用到了这个;调试
在python中,异常最多见的几种角色:日志
默认异常处理器:打印标准出错消息。这些消息包括引起的异常、堆栈跟踪、用户定义的异常等。code
假如try中,代码块有多个错误,可是,当遇到第一个错误时就停止,直接执行except;故try中的第二个错误就会捕捉不到;对象
try: print('try...') r = 10 / 0 print('result:', r) except ZeroDivisionError as e: # python3中,须要用as print('except:', e) finally: print ('finally...') print ('END')
用try来运行这段代码,若是执行出错,则后续代码不会继续执行,而是直接跳转至错误处理代码,即except语句块,执行完except后,若是有finally语句块,则执行finally语句块,至此,执行完毕。
若是有多个except,前面匹配到了错误,则后面的except不会捕获错误。例如:
try: print('try...') r = 10 / 0 print('result:', r) except BaseException as e: print('except1:',e) except ZeroDivisionError as e: print('except2:', e) finally: print ('finally...') print ('END')
从上面例子中,能够发现不管什么错误,都是except1捕获。由于Python的错误其实也是class,全部的错误类型都继承自BaseException,任何错误都是BaseException的子集;
python3.6中异常关系以下:
BaseException +-- SystemExit +-- KeyboardInterrupt +-- GeneratorExit +-- Exception +-- StopIteration +-- StopAsyncIteration +-- ArithmeticError | +-- FloatingPointError | +-- OverflowError | +-- ZeroDivisionError +-- AssertionError +-- AttributeError +-- BufferError +-- EOFError +-- 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 +-- SyntaxError | +-- IndentationError | +-- TabError +-- SystemError +-- TypeError +-- ValueError | +-- UnicodeError | +-- UnicodeDecodeError | +-- UnicodeEncodeError | +-- UnicodeTranslateError +-- Warning +-- DeprecationWarning +-- PendingDeprecationWarning +-- RuntimeWarning +-- SyntaxWarning +-- UserWarning +-- FutureWarning +-- ImportWarning +-- UnicodeWarning +-- BytesWarning +-- ResourceWarning
由于错误是class,捕获一个错误就是捕获到该class的一个实例。所以,错误并非凭空产生的,而是有意建立并抛出的。Python的内置函数会抛出不少类型的错误,咱们本身编写的函数也能够抛出错误。
若是要抛出错误,首先根据须要,能够定义一个错误的class,选择好继承关系,而后,用raise语句抛出一个错误的实例:
自定义异常,print一个对象,实际是两个步骤:1.执行该对象的__str__方法;2.print获得该对象的__str__的返回值,打印
class FooError(Exception): def __init__(self,msg): self.message = msg def __str__(self): return self.message try: raise FooError('自定义异常FooError.') except FooError as e: print(e)
原版:
# err.py class FooError(Exception): #定义错误的class,并指定父类; pass def foo(s): n = int(s) if n==0: raise FooError('invalid value: %s' % s) return 10 / n
这时,执行该脚本,会报出异常:
Traceback (most recent call last): File "/root/test02.py", line 22, in foo(0) File "/root/test02.py", line 19, in foo raise FooError('invalid value: %s' % s) main.FooError: invalid value: 0
通常咱们都尽可能使用python自带的异常类型便可。
改进版:
def foo(s): n = int(s) return 10 / n def bar(s): try: return foo(s) except BaseException as e: print ('Error!',e) raise #仅仅捕获错误,不知道应该怎么处理该错误,因此,最恰当的方式是利用raise继续往上抛,让顶层调用者去处理。 def main(): bar('0') main()
raise语句若是不带参数,就会把当前错误原样抛出。此外,在except中raise一个Error,还能够把一种类型的错误转化成另外一种类型:
try: 10 / 0 except ZeroDivisionError: raise ValueError('input error!')
这里将ZeroDivisionError 异常类型转换为了 ValueError。虽然能够转换,可是必定不要转换为绝不相关的异常类型。
经过raise,抛出指定异常给Exception,能够和Exception一块儿记录到日志中;
def db(): try: pass # 链接数据库,操做 except Exception as e: return False else: return True def log(n): print(n) pass # 打开文件,记录日志 def make(): try: result = db() if not result: raise Exception("数据库操做失败.") # 这里是将错误信息抛给Exception,这样能够经过下面log()记录到日志文件中。 except Exception as e: str_error = str(e) log(str_error) make()
咱们写代码,都不可能一次搞定。调试时若是使用print语句,那么调试完毕后,代码中处处都是print,咱们还得手动删除这些print。
其实,咱们能够用断言代替。凡是用print来辅助查看的地方,均可以用断言(assert)来替代:
def foo(s): n = int(s) assert n != 0, 'n is zero!!!' return 10 / n def main(): foo('0') main()
assert的意思是,表达式n != 0应该是True,不然,后面的代码就会出错。 若是断言失败,assert语句自己就会抛出AssertionError:
Traceback (most recent call last): ...... assert n != 0, 'n is zero!!!' AssertionError: n is zero!!! #注意,这里的异常信息是咱们断言处自定义的。
程序中若是处处充斥着assert,和print相比也好不到哪去。不过,启动Python解释器时能够用-O参数来关闭assert(把assert当作pass处理):
# python -O test02.py
代码调试时把print替换为logging,和assert比,logging不会抛出错误,并且能够输出到文件:
# err.py import logging logging.basicConfig(level=logging.DEBUG) s = '2' n = int(s) logging.info('n = %d' % n) print (10 / n)
记住,日志很重要!
此外,咱们还能够利用启动Python的调试器pdb,让程序以单步方式运行,能够随时查看运行状态。这里再也不详细讲解这个啦。