# 异常
当**意外的**情况在你的程序中发生时就会产生异常。例如,当你尝试读取一个文件但它并不存在时,或者当你要删除一个正在运行的程序的时候,这类情况会通过引发**异常**来处理。
类似地,如果你的程序有一些无效的语句,Python也会**抛出**错误提示告诉你这里有一些**错误**。
## 错误
我们来看一下一个简单的`print`函数。如果我们把`print`写成了`Print`会怎样?注意字母的大小写。这是Python会**抛出**一个语法错误。
```python
>>> Print("Hello World")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'Print' is not defined
>>> print("Hello World")
Hello World
```
我们注意到抛出了一个`NameError`的错误,以及这个错误发生的位置。这就是当错误发生的时候**错误处理程序**所做的事情。
## 异常
我们将**尝试**读取用户的输入。我们输入下面的第一行代码并按下`Enter`执行。当你的计算机提示你输入时,在 Mac 上按下 [Ctrl-d] 或者在 Windows 上按下 [Ctrl-z] 来观察会发生什么(如果你使用的是 Windows 系统而以上两个选择都无效时,你可以尝试在命令行窗口使用 [Ctrl-c] 来产生 KeyboardInterrupt 错误)。
```python
>>> s = input('请输入 --> ')
Enter something --> Traceback (most recent call last):
File "<stdin>", line 1, in <module>
EOFError
```
Python抛出了一个名为`EOFError`的错误信息,他的是*end of file*的缩写(由`Ctrl-d`触发),这是我们的程序刚开始的时候没有预料到的。
## 异常处理
我们可以用`try..except`语句来处理异常。我们将正常执行的语句放入 try 代码块中,然后将异常处理程序放入 except 代码块中。
例如 (保存为 `exceptions_handle.py`):
```python
try:
text = input('请输入 --> ')
except EOFError:
print('为什么你按下了EOF?')
except KeyboardInterrupt:
print('你取消了操作')
else:
print('你输入了 {}'.format(text))
```
输出为:
```shell
# 按下 Ctrl + d
$ python exceptions_handle.py
请输入 --> 为什么你按下了EOF?
# Press Ctrl + c
$ python exceptions_handle.py
请输入 --> ^C你取消了操作
$ python exceptions_handle.py
请输入 --> No exceptions
你输入了 No exceptions
```
**它是如何工作的:**
我们将所有的可能会引发异常或错误的语句写在 `try` 代码块中,然后将对应的异常处理程序写在 `except` 代码块中。每个`except`语句可以处理一个特定的异常或错误,也可以是一个异常或错误的列表(用括号表示)。如果没有提供异常或错误的名字,那么它会处理 _所有的_ 异常和错误。
请注意,每一个 `try` 语句至少应该有一个与之匹配的 `except` 语句,否则 try 语句就没有意义了。
如果你的程序发生了异常或错误,但是没有被处理,那么 Python 语言就会启动默认的异常处理程序,它会中止程序的运行,并且打印出一条异常信息。我们在之前的操作中已经见过了。
你也可以给你的 `try..except` 写上一个 `else` 代码块,当没有任何异常发生的时候就会执行 `else` 语句的内容。
在下面的例子中,我们将会学习如何获得异常对象,以便于我们能够得到关于异常额外的信息。
## 抛出异常
你可以使用 `raise` 语句 _抛出_ 一个异常。在语句中你需要提供异常或错误的名称,以及被抛出(_thrown_)的异常对象。
你抛出的异常或错误应该是一个直接或间接地从 `Exception` 派生的类。
例如:(保存为`exceptions_raise.py`):
```python
class ShortInputException(Exception):
'''用户自定义的异常类'''
def __init__(self, length, atleast):
Exception.__init__(self)
self.length = length
self.atleast = atleast
try:
text = input('Enter something --> ')
if len(text) < 3:
raise ShortInputException(len(text), 3)
# 其他代码在这里可以正常执行
except EOFError:
print('Why did you do an EOF on me?')
except ShortInputException as ex:
print(('ShortInputException: The input was ' +
'{0} long, expected at least {1}')
.format(ex.length, ex.atleast))
else:
print('No exception was raised.')
```
输出为:
```shell
$ python exceptions_raise.py
Enter something --> a
ShortInputException: The input was 1 long, expected at least 3
$ python exceptions_raise.py
Enter something --> abc
No exception was raised.
```
**它是如何工作的:**
在这里我们创建了我们自己的异常类,新的异常类被命名为 `ShortInputException` 。它有两个字段: `length` 表示输入内容的长度, `atleast` 表示程序期望的最小长度。
在 `except` 语句中,我们通过 `as` 指定一个变量保存抛出的异常或错误的对象。这类似于函数调用中的变量和参数。在这个特定的 `except` 语句中,我们使用异常对象的 `length` 和 `atleast` 字段构造了一个异常提示信息,让用户了解为什么会抛出这个异常。
## Try ... Finally
假设你要在你的程序中读取一个文件,你怎样保证无论是否有异常抛出,文件对象都被正确的关闭呢?我们可以使用 `finally` 语句块做到这一点。
例如:(保存为 `exceptions_finally.py` )
```python
import sys
import time
f = None
try:
f = open("poem.txt")
# 通常我们读取文件会采取这种形式
while True:
line = f.readline()
if len(line) == 0:
break
print(line, end='')
sys.stdout.flush()
print("Press ctrl+c now")
# 使得程序停顿一下在继续运行
time.sleep(2)
except IOError:
print("Could not find file poem.txt")
except KeyboardInterrupt:
print("!! You cancelled the reading from the file.")
finally:
if f:
f.close()
print("(Cleaning up: Closed the file)")
```
输出为:
```shell
$ python exceptions_finally.py
Programming is fun
Press ctrl+c now
^C!! You cancelled the reading from the file.
(Cleaning up: Closed the file)
```
**它是如何工作的:**
我们读取文件的内容,每读一行就让系统休息2秒,我们使用 `time.sleep` 函数让程序运行慢一点(通常情况下Python程序运行的飞快)。当程序还在运行的时候,按下 `ctrl + c` 键中止程序的运行。
我们注意到当程序退出的时候抛出了 `KeyboardInterrupt` 异常。然而,在程序退出之前,执行了 finally 代码块,并且文件对象被正确的关闭了。
请注意, Python 将变量中的 0 、 `None` 、空数组和空集合都视为 `False` 。这就是为什么我们可以在上面的代码中使用 `if f:` 。
还要注意,我们在 `print` 函数后面调用 `sys.stdout.flush()` 函数,这样可以及时输出结果。
## with语句
在 `try` 语句块中获取资源,然后再 `finally` 代码块中释放资源是一种常见做法,我们可以使用 `with` 简化一下程序的书写。
例如:(保存为`exceptions_using_with.py`)
```python
with open("poem.txt") as f:
for line in f:
print(line, end='')
```
**它是如何工作的:**
这段程序的输出应该和之前的例子是一模一样的。唯一的区别在与我们在 `with` 语句中使用 `open` 函数打开文件,通过使用 `with open` 系统会自动关闭这个文件。
实际的处理过程是这样的,`with` 语句会获取 `open` 函数返回的对象,我们假定这个对象名称是 “thefile”。
它 _总是会_ 在进入 `with` 代码块之前调用 `thefile.__enter__` 函数,并且 _总是会_ 在语句块的最后调用 `thefile.__exit__` 函数。
这样的话我们之前在 `finally` 代码块中写的程序就会自动的在 `__exit__` 方法中被执行,这种方式可以防止我们频繁使用 `try..finally` 语句。
关于这个主题更多的讨论已经超出了本书的范畴,请参考[PEP 343](http://www.python.org/dev/peps/pep-0343/)。
## 总结
本章我们讨论了 `try..except` 和 `try..finally` 语句,我们还自定义了一个我们自己的异常类型,并且在程序中将其抛出。
下一步,我们将会浏览一下Python标准库。
--------------------------------------------------
### 继续阅读[标准库](stdlib.md)
- 开始学习
- 搭建Python开发环境
- 简明Python教程
- 致敬
- 前言
- 关于Python
- 安装
- 第一步
- 基础
- 运算符和表达式
- 控制流
- 函数
- 模块
- 数据结构
- 实战案例
- 面向对象编程
- 输入与输出
- 异常处理
- 标准库
- 更多
- 继续学习
- 附录:免费/自由和开放源码软件
- 附录: 关于
- 附录: 版本历史
- 附录: 翻译
- 附录: 参与翻译工作
- 反馈
- Django Step Sy Step
- 第一讲 从简单到复杂
- 第二讲 做加法的例子
- 第三讲 使用Template
- 第四讲 生成csv格式文件
- 第五讲 session示例
- 第六讲 wiki的例子
- 第七讲 通讯录的例子
- 第八讲 文件导入和导出
- 第九讲 通讯录的美化
- 第十讲 扩展django的模板
- 第十一讲 用户管理
- 第十二讲 搜索和部署
- 第十三讲 Ajax的实现(一)
- 第十四讲 Ajax的实现(二)
- 第十五讲 i18n的一个简单实现
- 第十六讲 自定义Calendar Tag
- 第十七讲 View,Template和Tag
- Django开发实战
- Python开发规范
- Django项目的gitignore
- 怎样配置开发环境的settings
- 如何使用Django和Vue.js构建项目
- 使用WebSocket开发网页聊天室
- 怎样使Django Admin显示中文
- 怎样使Model在Admin界面中显示中文
- 使用Django Admin怎样上传并显示图片
- 解决Django模板和Vue指令花括号冲突的问题
- 使用Django和Vue开发微信公众号
- 使用Django和Vue调用微信JSSDK开发微信支付