Python 错误和异常

更新日期: 2019-08-27阅读: 2k标签: Python

错误和异常

在编写代码的时候,先不管出于什么原因,在运行阶段,可能都看到过一些错误的信息。这些信息当中(至少)有两种可区分的错误:语法错误 和 异常


语法错误

语法错误也叫解析错误,这可能在学习编码中最容易遇到的错误:

>>> while True print('Hello World')
  File "<stdin>", line 1
    while True print('Hello World')
                   ^
SyntaxError: invalid syntax

当出现错误时,解释器会输出出现语法错误的那行,同时会显示 ^ 符号标记检测到的错误。符号标记的位置表示错误出现在此处(或表示至少错误是在此处被检测出)。在这个示例中,print() 函数被检测出错误,这是因为前面的条件语句少了个冒号(:)。在错误信息中,会出现行号以及文件名,这都能方便在出现错误的时候到相应的位置检查。


异常

有时候,检查编写的代码发现并没有什么语法错误,但在执行时,还是会引发错误。这种在执行时检测到的错误被称为异常。大多数异常并不会被程序处理,会显示如下的错误信息:

>>> 10 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero

>>> x + 4
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined

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

如上示例,错误信息最后一行会告诉我们程序遇到的是什么类型的错误。在上面的例子中,出现的异常的类型分别是:ZeroDivisionError、NameError 和 TypeError。类型后面部分的信息则表示根据异常类型及其原因提供详细的信息。


异常处理

python 内置 try...except... 错误处理机制。

用例子看下 try...except... 机制:

try:
    r = 10 / 0
    print('r=', r)
except ZeroDivisionError as e:
    print('except:', e)

print('END')

程序运行结果如下:

except: division by zero
END

在这里说明一下 try 语句的工作原理:

  • 首先,执行 try 子句(也就是 try 和 except 关键字之间的语句)。
  • 如果这个时候没有异常产生,则会跳过 except 子句,完成 try 语句的执行。
  • 如果在执行 try 子句的时候发生异常,则会跳过该子句中剩下的部分(例如这个例子中,10 / 0 会产生异常,后面的 print('r=', r) 语句并没有执行)。这个时候,如果异常的类型与 except 关键字后面的异常类型匹配,则会执行 except 子句。最后继续执行整个 try 语句之后的代码(例如本例子中 print('END'))。
  • 如果发生的异常和 except 关键字后面指定的异常不匹配,则会向外部进行传递;若是没有找到处理程序,则认定为未处理异常,执行的时候将会停止,并显示前面提及的错误信息。

在这个例子当中, try 子句中的 10 / 0 出现异常,故后面的 print() 子句的内容并没有输出。然后,与 except 后面的异常匹配,所以输出异常信息 except: division by zero,最后输出整个 try 语句之后的 print('END') 语句。

这就是 try...except... 捕获异常处理异常的大致原理。

try...except 语句后面还有一个可选的 else 子句,使用时放在 except 子句后面。在 try 子句不发生异常时执行。对前面的例子进行一些修改,比如:

try:
    r = 10 / 1
    print('r=', r)
except ZeroDivisionError as e:
    print('except:', e)
else:
    print('No error!')

print('END')

最终输出的结果:

r= 10.0
No error!
END


抛出异常

raise 语句允许编码者强制抛出指定异常。例如:

>>> raise NameError('An error occurred here')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: An error occurred here

raise 唯一的参数就是要抛出的异常。这个参数必须是一个异常实例或者是一个异常类(派生自 Exception 的类)。

如果是打算确定是否发生了异常,但不打算处理的情况下,可以使用简单的 raise 语句形式重新引发异常。比如:

>>> try:
...     raise NameError('An error occurred here.')
... except NameError:
...     print('An exception flew by!')
...     raise
...
An exception flew by!
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
NameError: An error occurred here.


自定义异常类

程序可以通过创建新的异常类来命名自己的异常。自定义异常类通常直接或间接从 Exception 类派生。

class Error(Exception):
    '''基类
    '''
    pass

class InputError(Error):
    '''输入表达式错误时引发异常

    Attributes:
        expression -- 输入表达式
        message -- 异常输出的信息
    '''
    def __init__(self, expression, message):
        self.expression = expression
        self.message = message

class TransitionError(Error):
    '''状态转换不被允许时抛出异常

    Attributes:
        previous -- 转换前的状态
        next -- 转换后新的状态
        message -- 状态转换不被允许的说明
    '''
    def __init__(self, previous, next, message):
        self.previous = previous
        self.next = next
        self.message = message

一般情况下,自定义异常名称都以 Error 结尾,类似于标准异常的命名。


定义清理操作

try 语句还有一个可选子句 finally。用于定义必须在所有情况下都执行的清理操作。例如:

>>> def divide(a, b):
...     try:
...         res = a / b
...     except ZeroDivisionError:
...         print('division by zero!')
...     else:
...         print('res =', res)
...     finally:
...         print('executing finally clause')
...
>>> divide(10, 1)
res = 10.0
executing finally clause

>>> divide(10, 0)
division by zero!
executing finally clause

>>> divide('10', '1')
executing finally clause
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

在这里,可以看到,无论什么情况,finally 最终都会被执行。最后的例子中,由于两个字符串相除并没有被 except 子句处理,这里也在执行 finally 子句后,抛出 TypeError 异常。

finally 子句对于释放外部资源(比如读写文件或者网络连接)非常有用,无论是否成功使用资源。


预定义的清理操作

先看一段示例代码:

for line in open('file.txt'):
    print(line, end='')

其实这段代码有一个问题,它在这部分代码执行结束之后,会在一段不确定的时候,让 file 对象一直保持打开的状态。这里可能对简单的脚本并没有太大的影响,但若是程序足够大的话,这里将会是个问题。

python 提供一个 with 语句,允许支持上下文管理协议的对象能够确保它们得到及时和正确的清理:

with open('file.txt') as f:
    for line in f:
        print(line, end='')

在这里,文件 f 始终会被关闭,不至于浪费资源。


来源

  1. "8. Errors and Exceptions".docs.python.org.Retrieved 24 February 2020.
  2. "Exception hierarchy".docs.python.org.Retrieved 25 February 2020.
欢迎关注微信公众号《书所集录》

链接: https://fly63.com/article/detial/7788

9 个用于前端开发的Python 框架:JavaScript 替代品

JavaScript 是 Web 开发领域里非常重要的技术之一,并且是每个开发者都必须掌握的技能。而我作为一个开始使用 Python 编程的人

30个非常实用的Python技巧

Python 是机器学习最广泛采用的编程语言,它最重要的优势在于编程的易用性。如果读者对基本的 Python 语法已经有一些了解,那么这篇文章可能会给你一些启发。作者简单概览了 30 段代码,它们都是平常非常实用的技巧

25个超有用的Python代码段

Python是一种通用的高级编程语言。用它可以做许多事,比如开发桌面 GUI 应用程序、网站和 Web 应用程序等。作为一种高级编程语言,Python 还可以让你通过处理常见的编程任务来专注应用程序的核心功能。

如何选择异步Web Python框架

Python在3.4引入了 asyncio 库,3.6新增了关键字 async 和 await ,此后,异步框架迅速发展了起来,性能上能和Node.js比肩,除非是CPU密集型任务,否则没有理由不适用异步框架

11 个优秀的 Python 编译器和解释器

Python 是一门对初学者友好的编程语言,是一种多用途的、解释性的和面向对象的高级语言。它拥有非常小的程序集,非常易于学习、阅读和维护。其解释器可在Windows、Linux 和 Mac OS 等多种操作系统上使用

写 Python 代码不可不知的函数式编程技术

在 Python 中,函数是「头等公民」(first-class)。也就是说,函数与其他数据类型(如 int)处于平等地位。因而,我们可以将函数赋值给变量,也可以将其作为参数传入其他函数

Python 中 3 个不可思议的返回

Python 字典通过检查键值是否相等和比较哈希值来确定两个键是否相同.具有相同值的不可变对象在Python中始终具有相同的哈希值.注意: 具有不同值的对象也可能具有相同的哈希值(哈希冲突)

Python 实现单例模式

而且上面这种方法只有第一次 get_instance() 的时候能给对象传递参数,总之有许多弊端。Python 提供了 __new__ 方法正好完美解决了这个问题,再加上锁,就能实现一个线程安全的单例模式:

常见Python的Web开发框架

web开发框架存在的意义就在于可以快速便捷的构建应用,而不用去在意那些没必要的技术细节,到2020年为止,基于Python创建的的web应用已经非常多了,国外知名的有youtube.com、instagram、reditt、国内有知乎、豆瓣等等

nodejs中怎么调用python函数?

每种语言都有自己的优势,互相结合起来各取所长程序执行起来效率更高或者说哪种实现方式较简单就用哪个,nodejs是利用子进程来调用系统命令或者文件,NodeJS子进程提供了与系统交互的重要接口,其主要API有: 标准输入

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!