python中的异常处理

python

python中的异常

一、异常基础

在python中,异常会根据错误自动地被触发,也能由代码触发和截获。 异常由四个语句进行处理:

  • try/except: 捕捉由python或你引起的异常,并恢复。

  • try/finally: 无论异常是否发生,执行清理行为。无论是否异常,最后都必须执行finally;

  • try/except/else/finally: 即先执行try,如果有异常,执行except;如果没有异常,执行else;最后,都执行finally;

  • raise: 抛出异常,手动在代码中触发异常。

  • assert: 断言,有条件地在程序员代码中触发异常。主要用于代码调式 或 强制程序满足某个条件,否则报错;

  • with/as: 在python2.6及后续版本中实现环境管理器。我们在文件打开自动关闭那里就用到了这个;

二、异常的角色

在python中,异常最常见的几种角色:

  • 错误处理
  • 事件通知
  • 特殊情况处理
  • 终止行为
  • 非常规控制流程

三、异常处理

默认异常处理器:打印标准出错消息。这些消息包括引发的异常、堆栈跟踪、用户定义的异常等。

假如try中,代码块有多个错误,但是,当遇到第一个错误时就中止,直接执行except;故try中的第二个错误就会捕捉不到;

四、举例

1、异常处理

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

2、抛出异常

因为错误是class,捕获一个错误就是捕获到该class的一个实例。因此,错误并不是凭空产生的,而是有意创建并抛出的。Python的内置函数会抛出很多类型的错误,我们自己编写的函数也可以抛出错误。

如果要抛出错误,首先根据需要,可以定义一个错误的class,选择好继承关系,然后,用raise语句抛出一个错误的实例:

案例1:自定义异常

  • 自定义异常,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)

案例2:raise向上级抛异常

  • 原版:

    # 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。虽然可以转换,但是一定不要转换为毫不相关的异常类型。

案例3:raise向Exception抛异常

  • 通过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()

3、代码调试

我们写代码,都不可能一次搞定。调试时如果使用print语句,那么调试完毕后,代码中到处都是print,我们还得手动删除这些print。

断言assert

其实,我们可以用断言代替。凡是用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

logging

代码调试时把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)

记住,日志很重要!

pdb

此外,我们还可以利用启动Python的调试器pdb,让程序以单步方式运行,可以随时查看运行状态。这里不再详细讲解这个啦。

以上是 python中的异常处理 的全部内容, 来源链接: utcz.com/z/386892.html

回到顶部