一、 错误和异常

程序运行时可能会碰到一些错误和异常,例如本该用英文冒号却用了中文冒号的情况,再比如出现了除数为 0、年龄为负数、数组下标越界等,这些错误或异常如果不能被发现并加以处理,可能会导致程序无法运行或返回结果有误。和大部分编程语言一样,Python 也提供了处理错误和异常的机制,可以让我们捕获并处理,让程序继续能够执行。借助异常处理机制,甚至在程序崩溃前也可以做一些必要的工作,例如将内存中的数据写入文件、关闭打开的文件、释放分配的内存等。

Python 异常处理机制会涉及 try、except、else、finally 这 4 个关键字,同时还提供了可主动引发程序异常的 raise 语句

Python使用称为异常 的特殊对象来管理程序执行期间发生的错误。每当发生让Python不知所措的错误时,它都会创建一个异常对象。如果你编写了处理该异常的代码,程序将继续运行;如果未对异常进行处理,程序将停止并显示traceback,其中包含有关异常的报告。

二、认识错误

python中的错误和异常实际上是两个概念,当我们说错误时,一般指的是语法错误,又叫句法错误或解析错误,也就是解析代码时出现的错误。当代码不符合 Python 语法规则时,Python解释器在解析时就会报 SyntaxError 语法错误,与此同时还会明确指出最早探测到错误的语句位置。语法错误多是开发者疏忽导致的,属于真正意义上的错误,是解释器无法容忍的,只有将程序中的所有语法错误全部纠正,程序才能执行。如果我们使用Pycharm等IDE来写代码的时候,这种低级的语法错误会被IDE用红色波浪线标注出来,会比较方便我们解决这些错误。

#例1
while True
    print('Hello world')
    break
#执行以上语句,会报错如下:

  File "D:\PyGuide\错误和异常\zy_exception.py", line 4
    while True
              ^
SyntaxError: invalid syntax

#报错内容解释:
1.第一行,告诉你出错的文件位置信息(文件“报错代码所在的文件路径”,行号:第4行)
2.第二行,告诉你出现错误的代码是哪句
3.第三行,用一个箭头指向第二行中,第一次检测到错误的位置
4.第四行,直译为 语法错误:无效语法
有了提示,结合python的语法规则,我们知道,问题在于,True后面没有加英文冒号,修改后再执行即可成功
#例2
print "Hello,World!"
#以上语句在python2中正确,但在我们安装的python3环境中print作为内置函数,后面需要跟(),报错如下

  File "D:\PyGuide\错误和异常\zy_exception.py", line 1
    print "Hello,World!"
          ^
SyntaxError: Missing parentheses in call to 'print'. Did you mean print("Hello,World!")?

#报错内容解释:
1.报错的格式是固定的,前面几行参考例1即可
2.最后一行,直译为,语法错误:调用“print”时缺少括号。你想写的是print(“hello,world!”)吗?

总结一些常见的语法错误,大家在写代码过程中多留意,尽量不要出现。

一、SyntaxError: invalid syntax(无效语法错误)
翻译,语法错误:无效语法

1.忘记在 if , elif , else , for , while , class ,def 等语句的末尾添加 “:”
2.混用了 = 和 ==,要牢记,一个等号 = 是赋值操作符而两个等号 == 才是等于比较操作
3.尝试使用++ 或者 -- 这类自增自减操作符,其他编程语言可以这样写,但python的写法是 += 和-=
4.尝试使用Python关键字作为变量名,Python3的关键字有:and, as, assert, break, class, continue, def, del, elif, else, except, False, finally, for, from, global, if, import, in, is, lambda, None, nonlocal, not, or, pass, raise, return, True, try, while, with, yield等


二、SyntaxError: invalid character '“' (U+201C) (无效字符错误) #这里以引号举例
翻译,语法错误:无效字符

1.该用英文符号的却用了中文符号,如引号、冒号等,人眼不易察觉,要关注IDE的提示。


三、SyntaxError: EOL while scanning string literal(字符串不完整错误)
翻译,语法错误:扫描字符串时出现行尾错误(EOL = End Of Line error 译为行尾错误)

1.通常是因为字符串两端的引号未正确匹配,没有成对出现,要检查是否有单引号、双引号、或三引号的闭合情况。
2.需要引用的字符串包括多行,却用了单引号或双引号,这种情况应该用三引号,单双引号中的内容不支持跨行。

三、认识异常

前面说过,python中的错误和异常实际上是两个概念,错误一般指语法错误,导致无法运行,异常则是运行时产生的问题。当然,也不要纠结到底是异常还是错误,这两个词经常会混用,在python里,SyntaxError也是一种异常类型,在中文语境里,异常也是一种错误。我们之所以要说这是两个概念,我理解的原因是,我们对异常进行异常处理后,程序能够继续运行,而对于错误,程序会无法运行。

异常的产生有两种情况。其一,当程序运行到有问题的代码处,会抛出(throw)异常,这种属于被动型抛出,你只能接受。其二,你可以主动地抛出异常,不过这里我们约定成俗的不叫抛出,而是叫引发(raise),主动地引发异常是通过 raise语句来实现的,一般情况下,是你通过if判断某个条件不满足后,主动的报异常。(这里还是提一句,学的时候明白抛出和引发的区别就可以,不要纠结,因为后续大家都会混用这些词,raise的异常也会叫做主动抛出异常)

不管是被动抛出的异常还是主动引发的异常,异常都需要进行处理,不处理的话,程序遇到异常就会终止执行。处理异常这件事,或者说处理异常的这个动作,我们称之为捕获(catch),捕获异常不仅可以把这个异常抓到(报告异常信息),还可以执行针对异常的处理动作。

引发异常和捕获异常我们都在后面讲,现在先来认识异常。异常有不同的类型,异常的报错信息最后一行会把异常类型的名称(内置的标志符)打印出来,下述示例中的异常类型依次是ZeroDivisionError、TypeError和NameError,当然,最后一行,除了异常类型名称外,程序还会结合出错原因,说明错误细节。

常见异常举例

#例1
print(1/0)
#上面这句代码的意思是输出用 1 除以 0 的结果,因为 0 作除数是不被允许的(小学常识),所以运行后会产生如下错误:

Traceback (most recent call last):
  File "D:\PyGuide\错误和异常\zy_exception.py", line 2, in <module>
    print(1/0)
ZeroDivisionError: division by zero

#报错内容解释:
1.前三行指出了报错的位置,最后一句表示出错的类型
2.最后一行,直译为,零除错误:被0除
#例2

print('2' + 2)
#以上代码将字符串"2"和整数型2进行+连接,python不支持 str+int这种操作
Traceback (most recent call last):
  File "D:\PyGuide\错误和异常\zy_exception.py", line 4, in <module>
    print('2' + 2)
TypeError: can only concatenate str (not "int") to str
#直译,类型错误:只能将str(而不是“int”)连接到str

print(2 + '2')
#以上代码只是把 '2' + 2 改成了 2 + '2",你会发现报错也改变,python先检测到哪种错误就报哪种错误。
Traceback (most recent call last):
  File "D:\PyGuide\错误和异常\zy_exception.py", line 4, in <module>
    print(2 + '2')
TypeError: unsupported operand type(s) for +: 'int' and 'str'
#直译,类型错误:加号不支持这样的操作数类型:int整数型 和 str字符型 (就是说不能用整数+字符)

#例3
print(4 + a*3)
#a应该是一个指向数值的变量,但是这里我们没有先定义a,直接就使用,会报错如下
Traceback (most recent call last):
  File "D:\PyGuide\错误和异常\zy_exception.py", line 4, in <module>
    print(4 + a*3)
NameError: name 'a' is not defined
直译,名称错误: 没有对'a'这个名称进行定义

四、内置的异常类型

在 Python 中,所有异常类型都是一个派生自BaseException类的实例,也就是说,所有的异常都算是BaseException异常,不过这个异常类型太宽泛,所以python又给出了很多细化的错误类型,参见下面得异常类型层次结构。以NameError(第22行)为例,它本身是NameError异常类型,它也属于Exception(常规错误) 异常类型,还属于BaseException异常类型。

BaseException             所有异常的基类         
 +-- SystemExit              解释器请求退出
 +-- KeyboardInterrupt          用户中断执行(通常是输入^C)
 +-- GeneratorExit            生成器(generator)发生异常来通知退出
 +-- Exception               常规错误的基类
      +-- StopIteration              迭代器没有更多值 
      +-- StopAsyncIteration              必须通过异步迭代器对象的__anext__()方法引发以停止迭代
      +-- ArithmeticError                 所有数值计算错误的基类
      |    +-- FloatingPointError             浮点计算错误
      |    +-- OverflowError                  数值运算超出最大限制
      |    +-- ZeroDivisionError              除(或取模)零 (所有数据类型
      +-- AssertionError                  断言语句失败
      +-- AttributeError                  对象没有这个属性
      +-- BufferError                    与缓冲区相关的操作时引发
      +-- EOFError                        没有内建输入,到达EOF 标记
      +-- ImportError                     导入失败
      |    +-- ModuleNotFoundError        找不到模块
      +-- LookupError                     无效数据查询的基类
      |    +-- IndexError                      序列中没有此索引(index)
      |    +-- 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                   Python 语法错误
      |    +-- IndentationError         缩进错误
      |         +-- TabError          Tab 和空格混用
      +-- SystemError              一般的解释器系统错误
      +-- TypeError               对类型无效的操作
      +-- ValueError              传入无效的参数
      |    +-- UnicodeError             Unicode 相关的错误
      |         +-- UnicodeDecodeError     Unicode 解码时的错误
      |         +-- UnicodeEncodeError     Unicode 编码时错误
      |         +-- UnicodeTranslateError  Unicode 转换时错误
      +-- Warning                       警告的基类
           +-- DeprecationWarning          关于被弃用的特征的警告
           +-- PendingDeprecationWarning   关于构造将来语义会有改变的警告
           +-- RuntimeWarning           可疑的运行行为的警告
           +-- SyntaxWarning            可疑的语法的警告
           +-- UserWarning              用户代码生成的警告
           +-- FutureWarning            有关已弃用功能的警告的基类
           +-- ImportWarning            模块导入时可能出错的警告的基类
           +-- UnicodeWarning           与Unicode相关的警告的基类
           +-- BytesWarning             bytes和bytearray相关的警告的基类
           +-- ResourceWarning           与资源使用相关的警告的基类。

下面举例一些经常遇到的异常类型和其原因,当前仅做了解即可,遇到时建议详细研究并掌握

1、AssertionError 当 assert 语句失败时将被引发。用户利用断言语句检测异常时,如果断言语句检测的表达式为假,则会引发这种异常。

2、KeyError KeyError是关键字错误,当在现有键集合中找不到指定的映射(字典)键时就会引发错误。这个异常主要发生在字典中,比如当用户试图访问一个字典中不存在的键时会被引发。

3、NameError NameError是当某个局部或全局名称未找到时将被引发,也就是指变量名称发生错误,比如用户试图调用一个还未被赋值或初始化的变量时会被触发。

4、ValueError 当操作或函数接收到具有正确类型但值不适合的参数,也就是值错误,比如想获取一个列表中某个不存在值的索引。

5、SystemError 当解释器发现内部错误,但情况看起来尚未严重到要放弃所有希望时将被引发。 关联的值是一个指明发生了什么问题的字符串(表示为低层级的符号)。

6、SyntaxError SyntaxError主要是因为当解析器遇到语法错误,比如少个冒号、多个引号之类的,编程时稍微疏忽大意一下就会出错,应该是最常见的一种异常错误了。

7、TypeError TypeError是类型错误,当一个操作或函数被应用于类型不适当的对象时将被引发。比如在要求 int 时却传入了 list就会导致错误。

8、IndexError 当序列抽取超出范围时将被引发,也就是索引超出范围,比如最常见下标索引超出了序列边界,比如当某个序列m只有三个元素,却试图访问m[4]。

9、StopIteration StopIteration为迭代器错误,由内置函数 next() 和 iterator 的 next() 方法所引发,用来表示该迭代器不能产生下一项。当访问至迭代器最后一个值时仍然继续访问,就会引发这种异常。

10、AttributeError AttributeError是属性错误,当属性引用或赋值失败时就会出现。比如列表有index方法,而字典却没有,所以对一个字典对象调用该方法就会引发该异常。

11、FileNotFoundError 当所请求的文件或目录不存在时将被引发,就是当用户试图以读取方式打开一个不存在的文件时就会出现错误。

12、TimeoutError 当一个系统函数发生系统级超时的情况下将被引发。

13、FileExistsError 当试图创建一个已存在的文件或目录时将被引发。