Python 错误与异常


2021年8月23日, Learn eTutorial
1891

在本教程中,您将通过示例掌握有关 Python 中错误和异常的所有知识。您还将学习如何抛出和断言异常。除此之外,您还将看到关于在异常处理中使用 try、except、else 和 finally 关键字的演示。

到目前为止,在我们的系列教程中,我们遇到了编写代码时发生的各种类型的错误。通常,Python 中存在两种可区分的错误类型:语法错误异常

Types of errors in Python

Python 中的错误类型

什么是语法错误

语法错误是在 Python 中编写程序时引起的结构性错误。在 Python 中,所有内容都以一种称为语法的特定结构定义。当不满足此语法时,解析器会将其检测为错误。因此,语法错误也称为解析错误。请看下面的例子

print('Hello ,World) 

输出错误

File "exception_ex.py", line 1
    print('Hello ,World)
                       ^
SyntaxError: EOL while scanning string literal

在此示例中,解析器检测到一个错误并在控制台中显示错误。让我们看看如何仔细检查错误。

  • 第一行给出了文件名和检测到错误的行号。
  • 第二行显示了错误语句,在这种情况下,在 print 函数处检测到错误。语句末尾的“小箭头 (^)”定位了解析器遇到语法错误的位置。
  • 第三行给你有关错误的线索。这里是在字符串文字后缺少 EOL,这意味着我们在 Hello World 后错过了闭合的单引号。

现在添加单引号并再次运行它,您将得到输出为 Hello, World

Python 中的异常是什么?

异常是在程序执行期间发生的错误类型,即使程序在语法上是正确的。这种类型的错误通过显示异常错误来中断程序的正常流程。请看下面的例子,展示了一些 Python 中不同类型的标准异常

在此示例中,我们遇到了 4 种异常错误,即 TypeErrorNameErrorZeroDivisionErrorFileNotFoundError。错误消息可以按如下方式仔细检查

与语法错误不同,异常依赖于由于逻辑失败而发生的错误。

  1. >>> 1+'2'
    
    

    输出错误

      Traceback (most recent call last):
      File "", line 1, in 
        1+'2'
    
    TypeError: unsupported operand type(s) for +: 'int' and 'str'
    
  2. >>> 1+two
    

    输出错误

    Traceback (most recent call last):
      File "", line 1, in 
        1+two
    NameError: name 'two' is not defined
    
  3. >>> 2/0
    

    输出错误

    Traceback (most recent call last):
      File "", line 1, in 
        2/0
    ZeroDivisionError: division by zero
    
  4. >>> open('Newfile' 
    

    输出错误

    Traceback (most recent call last):
      File "", line 1, in 
        open('Newfile')
    FileNotFoundError: [Errno 2] No such file or directory: 'Newfile'
    

 

  • 第一行给出文件名和行号
  • 第二行显示了有问题的语句
  • 最后一行指定了错误及其详细信息

 

与语法错误不同,异常依赖于由于逻辑失败而发生的错误。

Python 内置异常

异常可以分为两类,即

  • 内置异常
  • 用户自定义异常

内置异常是当程序中发生非法操作时引发的标准异常。要查看 Python 中所有可用的内置异常,我们可以使用内置函数 local(),如下所示

print(dir(locals()['__builtins__'])) 

其中,

(locals()['__builtins__']) 返回系统中的异常模块、函数等,dir 将它们列为字符串。

下面列出的是 Python 中用于处理运行程序时常见预期错误的内置异常。

异常 异常原因
AssertionError 当 assert 语句失败时引发。
AttributeError 当属性赋值或引用失败时引发。
EOFError 当 input() 函数遇到文件结束条件时引发。
FloatingPointError 当浮点运算失败时引发。
GeneratorExit 当生成器的 close() 方法被调用时引发。
ImportError 当找不到导入的模块时引发。
IndexError 当序列的索引超出范围时引发。
KeyError 当在字典中找不到键时引发。
KeyboardInterrupt 当用户按下中断键(Ctrl+C 或 Delete)时引发。
MemoryError 当操作耗尽内存时引发。
NameError 当在局部或全局作用域中找不到变量时引发。
NotImplementedError 由抽象方法引发。
OSError 当系统操作导致系统相关错误时引发。
OverflowError 当算术运算的结果太大而无法表示时引发。
ReferenceError 当使用弱引用代理访问垃圾回收的引用对象时引发。
RuntimeError 当错误不属于任何其他类别时引发。
StopIteration 由 next() 函数引发,以指示迭代器没有更多项目要返回。
SyntaxError 当解析器遇到语法错误时引发。
IndentationError 当存在不正确的缩进时引发。
TabError 当缩进包含不一致的制表符和空格时引发。
SystemError 当解释器检测到内部错误时引发。
SystemExit 由 sys.exit() 函数引发。
TypeError 当函数或操作应用于不正确类型的对象时引发。
UnboundLocalError 当在函数或方法中引用局部变量,但没有将值绑定到该变量时引发。
UnicodeError 当发生与 Unicode 相关的编码或解码错误时引发。
UnicodeEncodeError 当在编码过程中发生与 Unicode 相关的错误时引发。
UnicodeDecodeError 当在解码过程中发生与 Unicode 相关的错误时引发。
UnicodeTranslateError 当在翻译过程中发生与 Unicode 相关的错误时引发。
ValueError 当函数获得正确类型但值不当的参数时引发。
ZeroDivisionError 当除法或模运算的第二个操作数为零时引发。

我们也可以定义自己的异常,这被称为用户定义的异常。我们将在掌握类的概念后,在即将到来的教程中讨论用户定义的异常。

如何抛出异常

现在我们熟悉了当条件失败时会产生的内置异常。如果某些条件失败,也可以手动抛出异常。我们可以使用关键字 raise 来抛出这样的异常。这是一个例子

示例:如何抛出异常

age= 15
if age<18:
 raise Exception('Age must be greater than or equal to 18' 

输出错误

Traceback (most recent call last):
  File "Exception_ex.py", line 67, in 
    raise Exception('Age must be greater than or equal to 18')
Exception: Age must be greater than or equal to 18

我们还可以指定要引发的错误类型以及需要提供给用户的信息。

示例:如何抛出异常

age=int(input('Enter your age:'))  
if age<0:
 raise ValueError("Invalid age given as input!") 

输出错误

Enter your age: -2
Traceback (most recent call last):
  File "Exception_ex.py", line 72, in 
    raise ValueError("Invalid age given as input!")
ValueError: Invalid age given as input!

如何断言异常

Assert 语句用于调试。因此它测试一个条件并检查条件是否为真。如果为真,则程序正常执行。但如果 assert 条件失败,结果将是带有可选错误消息的 AssertionError 异常。请看下面的例子

示例:如何断言异常

age = 18

# True Condition 
assert age ==18
print(' Age is Valid')

#False condition
assert age ==15 

输出错误

Age is Valid

Traceback (most recent call last):
  File "Exception_ex.py", line 81, in 
    assert age ==15
AssertionError

在示例中,当条件评估为 false 时,程序终止并产生一个回溯,指出断言错误。与 raise 一样,您也可以在此处将错误定义为参数。观察以下示例以了解变化。


age=int(input('Enter your age :'))

assert (age>=18),'Age must be above or equal to 18'
print(' Age is Valid')

输出错误

Enter your age :15

Traceback (most recent call last):
  File "Exception_ex.py", line 78, in 
    assert (age>=18),'Age must be above or equal to 18'
AssertionError: Age must be above or equal to 18

简而言之,我们可以说断言语句,尽管处理异常,但被用作调试辅助工具。断言语句处理程序中不可恢复的错误并通知开发人员,这意味着它们旨在为程序中途可能出现的意外错误发出信号。当发生断言错误时,程序将停止,并保持静默,直到我们修复错误。

Python 如何处理异常

现在我们可以学习如何在 Python 中处理异常。内置异常和用户定义的异常都可以使用 try、except、else 和 finally 语句来处理。在编写代码时,最好使用异常语句来避免任何不必要的错误。

异常处理流程图

为了理解异常期间程序的流程,下面显示了一个基本的流程图。

Python Exception Handling Flowchart

Python 异常处理流程图

try...except 块

如果您在执行过程中怀疑某些错误或异常情况,最好的方法是将代码块放在“try”语句中。try 语句后的代码块照常执行,如果检测到任何错误,控制将迅速转移到“except”语句。except 语句作为异常处理程序捕获错误并对其进行管理。换句话说,except 语句响应在 try 语句中发生的异常。

try…. except 块的语法是

try:
 …  ….. ….. ...
    Run this code
    …  ….. ….. …

except  exception_1:
    …  ….. ….. ...
    Executes on detecting exception_1
    …  ….. ….. ... 

现在我们可以查看下面的例子,以更多地了解 try...except 块的工作原理。

该示例显示了如果没有异常,try 语句是如何工作的。

try:
 fo = open('New.txt','r')
 print(fo.read())
 fo.close()
except FileNotFoundError:
 print('File does not exist in the system!') 

在上面的例子中,没有异常,因此程序照常执行并打印它在文件中读取的内容。现在考虑有异常的情况会发生什么。

try:
 fo = open('File.txt','r')
 print(fo.read())
 fo.close()
except FileNotFoundError:
 print('File does not exist in the system!') 

在这个例子中,File.txt 是一个系统中不存在的文件,因此它引发了一个异常,程序流程转移到了一个 except 语句,在那里我们已经处理了当遇到 FileNotFoundError 时要打印的内容。因此打印出用户友好的输出。

如果您怀疑放置在 try 语句中的关键代码中有多个异常,您也可以添加任意多的 except 语句。

Try:
 print(2/0)
 fo = open('File.txt','r')
 print(fo.read())
 fo.close()
except ZeroDivisionError:
 print('Mathematically,division by zero is not defined!')
except FileNotFoundError:
 print('File does not exist in the system!') 

从这个例子中,很明显 try 语句会执行直到遇到第一个异常。

Try...except...else 块

我们可以通过在末尾包含一个可选的 else 子句来将 try..block 扩展到下一个级别。这明确了如果 try 语句没有任何异常该执行什么。显然,如果没有异常,try 块也能很好地工作。try ….except ...else 的语法形式如下

try:
 …  ….. ….. ...
    Run this code
    …  ….. ….. …

except  exception_1:
    …  ….. ….. ...
    Executes on detecting exception_1
    …  ….. ….. ...
else  :
    …  ….. ….. ...
    Executes when there is no exception
    …  ….. ….. ... 

观察下面给出的例子

try:
 print(2/1)
except ZeroDivisionError:
 print('Mathematically,division by zero is not defined!')
else:
  try:
   fo = open('New.txt','r')
   print(fo.read())
   fo.close()
  except FileNotFoundError: 

从输出中,我们可以理解 else 部分在这里执行,因为在初始的 try 语句中没有发现异常。解释器尝试打开文件并打印文件内容作为输出,因为该文件存在于系统中。

Try...finally 块

最后,您可以添加一个 finally 子句来在执行后清理代码。语法是

try:
        …  ….. ….. ...
        Run this code
         …  ….. ….. …

except  exception_1
         …  ….. ….. ...
         Executes on detecting exception_1
         …  ….. ….. ...
else  :
         …  ….. ….. ...
         Executes when there is no exception
         …  ….. ….. …
finally  :
       Executes always

让我们看看‘finally’子句在程序中是如何实现的。

#File.txt is not available in system, raises an exception
try:
 fo = open('File.txt','r')
 print(fo.read())
 fo.close()
except FileNotFoundError:
 print('File does not exist in the system!')
else:
 print('File exist in the system!')
finally:
 print('Always executes,irrespective of execution') 

让我们更改文件名,看看当系统中有文件可用时会发生什么。

#New.txt is available in system 
try:
 fo = open('New.txt','r')
 print(fo.read())
except FileNotFoundError:
 print('File does not exist in the system!')
else:
 print('File exist in the system!')
Finally:
 fo.close()
 print('Always executes,irrespective of execution') 

输出

Hi Welcome To Learn eTutorial
File exist in the system!
Always executes,irrespective of execution

在示例中,您可能会注意到 fo.close() 被放置在 finally 块中,这是因为 finally 结构确保了即使我们在中间遇到异常,文件也能被关闭。

使 finally 子句与其他语句不同的特殊之处在于,finally 子句总是执行,无论是否遇到执行错误。finally 子句促进了那些无论是否检测到异常都应该始终运行的代码块的执行。