错误与异常

章节

  1. 错误和异常
  2. 处理异常
  3. raise
  4. 自定义异常
  5. assert
  6. 总结

 

一、错误和异常

有两种类型的错误:语法错误 (syntax error) 和异常 (exception)

语法错误,也叫解析错误,解释器在解析语法时探测到的错误,必须在代码运行前纠正.

异常,尽管语句或语法上没有问题,但是在执行时候还是可能出现错误,这种在代码执行时探测到的错误称为异常,解释器无法执行

语法错误

顾名思义,检测到语法有错误,解释器会重复错误的语句,然后显示一个箭头指向出错的位置,打印出现错误的文件名与行数、错误的类型及信息.

《错误与异常》

异常

语法和语句上没有问题,但代码在执行时候还是可能出现错误,这些错误称之为异常,比如

IndexError:索引超出范围

《错误与异常》

KeyError:不存在的key

《错误与异常》

同样地,如果抛出异常,也会打印出文件的名称和行号、异常的语句、异常类型及信息

内建异常

Pythono内建了一些异常类型 ( 异常也是对象 ),它们的意义及继承的关系如下

《错误与异常》
《错误与异常》

BaseException           所有异常的根异常
 +-- SystemExit         程序退出/终止
 +-- KeyboardInterrupt  由键盘中断(通常为Ctrl+C)产生
 +-- GeneratorExit      由生成器.close()方法引发
 +-- Exception          所有非退出异常的基类
      +-- StopIteration    引发后可停止迭代(一般是迭代器中的元素都取完了)
      +-- ArithmeticError  算数异常的基类
      |    +-- FloatingPointError 浮点操作失败
      |    +-- OverflowError 
      |    +-- ZeroDivisionError   除数为0
      +-- AssertionError     由assert语句引发
      +-- AttributeError     当属性名称无效时引发
      +-- BufferError      
      +-- EnvironmentError   发生在Python外部的错误
      |    +-- IOError       文件I/O相关的错误
      |    +-- OSError       操作系统错误
      |         +-- WindowsError (Windows)
      |         +-- VMSError (VMS)
      +-- EOFError         到达文件结尾时引发
      +-- ImportError      import语句失败
      +-- LookupError      索引和键错误
      |    +-- IndexError  索引和键错误
      |    +-- KeyError    字典key不存在
      +-- MemoryError                      内存不足
      +-- NameError                        无法找到局部或全局名称
      |    +-- UnboundLocalError           未绑定的局部变量
      +-- ReferenceError                   销毁被引用对象后使用的弱引用
      +-- RuntimeError                     一般运行时错误
      |    +-- NotImplementedError         没有实现的特性
      +-- SyntaxError                      解析错误, 语法错误
      |    +-- IndentationError            缩进错误
      |         +-- TabError               使用不一致的制表符
      +-- SystemError                      解释器中的非致命系统错误
      +-- TypeError                        给操作传递了错误的类型
      +-- ValueError                       无效类型
      |    +-- UnicodeError                Unicode错误
      |         +-- UnicodeDecodeError     Unicode解码错误
      |         +-- UnicodeEncodeError     Unicode编码错误
      |         +-- UnicodeTranslateError  Unicode转换错误
      +-- Warning      警告的基类
           +-- DeprecationWarning   关于被弃用的特征的警告
           +-- PendingDeprecationWarning
           +-- RuntimeWarning    可疑的运行时行为(runtime behavior)的警告
           +-- SyntaxWarning     可疑的语法的警告
           +-- UserWarning       用户代码生成的警告
           +-- FutureWarning     关于构造将来语义会有改变的警告
           +-- ImportWarning     import的警告
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning

View Code

注:内建的异常类型,python2是放在exceptions模块,python3放在builtins模块.

《错误与异常》
《错误与异常》

>>> import exceptions
>>> dir(exceptions)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'EnvironmentError', 'Exception', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '__doc__', '__name__', '__package__']

exceptions
《错误与异常》
《错误与异常》

>>> import builtins
>>> dir(builtins)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']

builtins

二、处理异常

Errors should never pass silently (错误不能默默地通过,不要放过异常)

程序在执行过程中可能会出现各种异常,也就是我们所说的报错,如IndexError(列表越界)、 KeyError(字典键不存在)等。如果这些异常没有被处理,程序将终止运行,这样缺乏容错、对外暴露了错误的信息,所以要尽量考虑可能出现的异常并处理.

所以,异常处理目的是:增强程序的健壮及可靠性、支持容错、规避错误的信息(试想一下谁会用一大堆的报错的软件)

Python中使用try/except/finally/else语句来处理异常,用法

try:  
    可能出现的异常语句 except (异常类型1, 异常类型2...) as 异常实例名:
    匹配到了异常类型执行这里
......   可选多个except语句 else:  
    没有出现异常执行这里 finally:  
    任何情况下都会执行这里

注:try是必须的、可配合else或finally语句、as可以把异常实例绑定到一个名字,也是可选的

异常处理的流程是

  • 先执行try..except之间的语句
  • 如果出现异常
    • 被except后面的异常类型匹配到,则执行except后面的语句
    • 没有被except后面的异常匹配到,则传递给上层try,还是没有被处理那么就是一个未处理的异常,然后抛出异常
  • 如果没有出现异常,则忽略except后面的语句,往下面执行

通常有下面几种形式处理异常

try / except

>>> try:
...     1 / 0
... except ZeroDivisionError:
...     print('数字相除分母不能为0')
... 
数字相除分母不能为0
>>> try:
...   l = [1, 2, 3, 4]
...   l[4]
... except IndexError:
...   print('索引超过范围')
...
索引超过范围

except 后面可以通过as将一个名字绑定在异常实例上, 可选择指定异常参数,异常参数会存储在异常实例的args属性中

>>> try:
...    raise Exception('spam', 'eggs')  # 括号里面的是异常参数
... except Exception as inst:           # 把inst绑定到异常实例
...    print(type(inst))                # inst是Exception类的实例
...    print(inst.args)                 # 异常参数存储在args
...    print(inst)                      # 调用__str__方法打印出异常参数,也可以重写                         
...    x, y = inst.args                 # 解包args
...
<class 'Exception'>
('spam', 'eggs')
('spam', 'eggs')

except 后面可以同时指定多个异常类型,但只有一个会被匹配

>>> try:
...     1 / 0      # ZeroDivisionError
...     abc        # NameError
...     1 + '1'    # TypeError
... except (ZeroDivisionError, NameError, TypeError) as err:
...     print('异常信息: {}'.format(err))
... 
异常信息: integer division or modulo by zero

可以有多个except匹配不同异常类型

try:
    1 / 0       # ZeroDivisionError
    abc         # NameError
    1 + '1'     # TypeError
except ZeroDivisionError:
    print('异常: 数字相除分母不能为0')
except NameError:
    print('异常: 对象名不存在')
except TypeError as err:
    print('异常:{0}'.format(err))

异常: 数字相除分母不能为0

try /except /else

可选的else,只有当try中的语句没有出现异常才会执行else后面语句

try:
    f = open('filename', 'r')
except IOError:
    print('cannot open', filename)
else:
    print(filename, 'has', len(f.readlines()), 'lines')
    f.close()

建议try..except只放可能出现异常的代码, 正常的代码可以放到else

try / finally

try后面有个可选的finally,不管是否出现异常都执行finally后面的语句,通常用来执行清理或扫尾动作,比如操作文件或数据库,完成后释放连接

try:
    f = open(name)
    print(f.readlines())
except KeyboardInterrupt:
    pass
finally:                                 # 无论try中是否有异常都执行finally后的语句
    print('Good bye!')
    f.close()

当然,执行清理动作更多的还是使用with语句

try / except / else / finally

try:
    f = open('filename')
except IOError as err:
    print('Cannot open filename', err)
else:
    print(f.read())
finally:
    print('Good Bye')
    f.close()

try嵌套

try还可以嵌套,当内层的try无法处理异常时会传递给上层的try处理

>>> try:
        try:
            abc     # NameError
            1 / 0   # ZeroDivisionError
        except ZeroDivisionError:
            print('异常: 分母不能为0')
    except Exception as err:
        print('异常: {0}'.format(err))    
异常: name 'abc' is not defined

匹配任意异常

Exception是一般异常的基类,几乎可以匹配任意的异常类型,如果不能精确地知道会抛出什么异常类型可以使用它匹配

>>> try:
...     l = [1, 2, 3]
...     2 / 0                            # ZeroDivisionError
...     l[4]                             # IndexError
... except Exception:
...     print('任何异常都可以捕获并处理')
... 
任何异常都可以捕获并处理

如果except后面没有任何异常类型,则匹配可以匹配所有的异常,不建议这样做

try:
    1 / 0
    f = open('xxxoo')
except:
    print('异常了')

不建议匹配任意异常,应该精确的匹配异常

语法错误不能被处理

语法错误不能被异常处理,因为语法错误是代码运行前的错误,代码还没有运行

try:
    while 1 print('hello word!')
except SyntaxError:
    print('处理语法错误')        # 不会被执行

  File "<stdin>", line 2
    while 1 print('hello word!')
                ^
SyntaxError: invalid syntax

三、raise

raise允许引发特定的异常传递异常

引发特定的异常

>>> raise NameError           # 触发NameError异常
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
NameError
>>>
>>> raise NameError('HiThere')  # 触发NameEroor异常,带参数
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: HiThere

 raise后面必须跟一个异常类或异常类的实例  (这个异常类必须直接或间接继承于Exception)

传递异常

如果你只是捕获异常又不准备处理它,可以重新抛出异常

>>> try:
...     raise NameError('引发NameError异常')
... except NameError:
...     print('重新传递NameError异常')
...     raise        # raise后面不要跟任何语句                  
...

重新传递NameError异常
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
NameError: 引发NameError异常

四、自定义异常

如要想自定义异常,只需要直接或间接的继承Exception类,通过继承也可以覆蓋或添加功能

class MyError(Exception):
    """Base exception class"""
def __init__(self, msg=''): self.msg = msg

>>> MyError('触发我的异常!') # __repr__ MyError('触发我的异常!',) >>> raise MyError('触发我自己的异常!') Traceback (most recent call last): File "<stdin>", line 1, in <module> __main__.MyError: 触发我自己的异常! >>> try: ... raise MyError('触发MyError异常!') ... except MyError: ... print("处理MyError异常") ... 处理MyError异常

上面例子覆蓋了Exception类的__init__、__repr__、__str__方法

实际项目中可能会自定义很多异常,常见的做法是创建一个异常基类,然后所有的异常类都继承它(自定义异常一般以Error结尾)

class Error(Exception):
    """Base exception class"""
    def __init__(self, msg=''):
        self.msg = msg
def __str__(self): # 可以省略,因为父类中已有这个方法 return self.msg class InputError(Error): """InputError exception class""" pass class OutputError(Error): """OutputError exception class""" pass >>> raise InputError('InputError') Traceback (most recent call last): File "<stdin>", line 1, in <module> __main__.InputError: InputError >>> >>> raise OutputError('OutputError') Traceback (most recent call last): File "<stdin>", line 1, in <module> __main__.OutputError: OutputError

五、assert

Assert statements are a convenient way to insert debugging assertions into a program.  

可以在程序里面插入一些调试 ,判某些逻辑是否符合你的预期

用法:  判断表达式的真假,如果为False则触发AssertionError异常, 为True则什么都不干

assert expression [, arguments]
或者
assert(expression) [, arguments]

等价于

if not expression:
    raise AssertionError

例子

>>> assert 1 == 1   # assert(1==1)
>>> assert True     # assert(True)
>>> assert(1 + 1) < 2         
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

还可以加上异常参数

《错误与异常》
《错误与异常》

>>> assert 1 + 1 < 2, '判断条件为Fasle'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError: 判断条件为Fasle

>>> assert len([1,2,3]) == 4, repr('判断条件为Fasle')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError: '判断条件为Fasle'

View Code

判断文件有没有内容 

《错误与异常》
《错误与异常》

with open('testfile') as f:
    lines = f.readlines()                   
    assert lines, repr('no data')              # 如果lines为空则抛出异常
    print([line.upper() for line in lines])

View Code

函数测试

《错误与异常》
《错误与异常》

>>> def test_add(a, b):
...     return a + b
... 
>>> assert test_add(1, 2) == 3
>>> assert test_add(1, 9) == 9, repr('%s != %s' % (10, 9))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError: '10 != 9'

View Code

六、总结

  • 如果异常没有被处理,程序就会终止执行,对异常处理的目的主要是提高代码的健壮、可靠性,允许程序容错,规避内部的错误细节
  • 异常处理的流程是先执行try与except之间的语句,如果出现异常则匹配except后面的异常类型,匹配到了就执行后面的语句,如果没有匹配到则往上抛,如果还没有对这个异常进行处理,那么程序就会终止运行
  • 尽量只把可能出现异常的代码放到try..except中,  正常的代码放到else语句块中
  • rasie语句可以引发和传递异常、assert是断言,测试某些功能是否符合你的语句
  • 可选的finally一般用来执行清理动作
  • 不要放过异常,要精确的捕获异常,少用 except 来捕获所有的异常
  • 异常也是一个对象,自定义异常可以直接或间接继承Exception类

参考: http://docspy3zh.readthedocs.io/en/latest/tutorial/errors.html

    原文作者:opss
    原文地址: https://www.cnblogs.com/huangweimin/articles/6509673.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞