forked from xingkeyu/byte_of_python
-
Notifications
You must be signed in to change notification settings - Fork 0
/
15-exceptions.pd
200 lines (141 loc) · 7.19 KB
/
15-exceptions.pd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# 异常处理
在你的程序中出现某些*异常*情况时,异常发生。例如,如果你要读一个文件而文件不存在?或者如果你不小心删除了正在运行的程序?这种情况的处理使用**异常**处理。
同样,如果您的程序有一些无效的语句会怎样呢?这是由Python以**举起**它的句柄,告诉你有一个**错误**的方式处理。
## 错误
考虑一个简单的`print`函数调用。如果我们把`print`错拼为`Print`,注意大小 写。在这种情况下,Python会*提出*一个语法错误。
~~~
>>> Print('世界你好')
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
Print('世界你好')
NameError: name 'Print' is not defined
>>> print('世界你好')
世界你好
~~~
观察到出现了一个`NameError`,且错误发生的地方也被打印出来,这就是为这个错误,*错误处理程序*所做的。
## 异常
我们将**尝试**读取来自用户的输入。按`ctrl-d`后看看会发生什么?
~~~
>>> s = input('输入一些东西 --> ')
输入一些东西 -->
Traceback (most recent call last):
File "<pyshell#3>", line 1, in <module>
s = input('输入一些东西 --> ')
EOFError: EOF when reading a line
~~~
Python会产生一个称为`EOFError`的错误,这基本上意味着它发现了一个不希望看到的一个*文件结束符号* (这是由`ctrl-d`表示)。
## 处理异常
我们可以使用`try..except`语句处理异常,我们基本上把通常的语句放在try块,把所有的错误处理程序放在except块。
例子 (保存为 `try_except.py`):
~~~python
try:
text = input('输入一些东西 --> ')
except EOFError:
print('为什么你要输入文件结束符呢?')
except KeyboardInterrupt:
print('你取消了操作。')
else:
print('你输入了 {0}'.format(text))
~~~
Output:
~~~
$ python3 try_except.py
Enter something --> # 按ctrl-d
为什么你要输入文件结束符呢?
$ python3 try_except.py
Enter something --> # 按ctrl-c
你取消了操作。
$ python3 try_except.py
Enter something --> 没有例外
你输入了 没有例外
~~~
它是如何工作的:
我们把所有的可能导致异常/错误的语句放在`try`块中,然后把适当的错误/异常处理程序放在`except`子句/块中。`except`子句可以处理一个指定的错误或异常,或一个圆括号括起来的错误/异常列表。如果没有提供错误或异常的名字,它将处理*所有*错误和异常。
注意,每一个`try`子句,必须有至少一个`except`子句与之相对应,否则,要一个试try块有什么用?
如果不处理任何错误或异常,那么默认的Python处理程序被调用,它只是停止程序的执行和输出错误消息。这点在上面我们已经看到了。
与`try..except`块相关,也可以使用`else`子句,没有例外发生的时候执行`else`子句。
在下面的例子中,我们还将看到如何获得异常对象,以便我们重新得到附加的信息。
## 提出异常
你可以使用`raise`语句通过指定错误/异常的名字*提出*异常,异常对象也被*抛出*。
你可以提出的错误或异常应该是一个类,它必须直接或间接地是一 `Exception` (异常)类的一个派生类。
例子 (保存为 `raising.py`):
~~~python
class ShortInputException(Exception):
'''一个用户定义的异常类。'''
def __init__(self, length, atleast):
Exception.__init__(self)
self.length = length
self.atleast = atleast
try:
text = input('输入一些东西 --> ')
if len(text) < 3:
raise ShortInputException(len(text), 3)
# 像通常一样可以继续其它的工作
except EOFError:
print('为什么输入文件结束符?')
except ShortInputException as ex:
print('输入短的例外: 输入有 {0} 长, 预期至少 {1}'\
.format(ex.length, ex.atleast))
else:
print('没有异常提出。')
~~~
Output:
~~~
$ python3 raising.py
输入一些东西 --> 中国
输入短的例外: 输入有 2 长, 预期至少 3
$ python3 raising.py
输入一些东西 --> 中国功夫
没有异常提出。
~~~
它是如何工作的:
这里,我们创建了我们自己的异常类型,叫做`ShortInputException`,它有两个字段-输入字符串的长度 `length` 和程序期望的最小长度`atleast`。
在 `except` 子句,我们提到被保存`为`变量名字的错误的类,控制相应的错误/异常对象,类似于在函数调用的的实参和形参。 在这个特别的`except`子句中,我们使用异常对象的`length`和`atleast`字段,给用户打印一个适当的消息。
## Try .. Finally
假设在你的程序中正在阅读一个文件,你如何确保文件对象是正常关闭,是否产生了常?这可以通过使用`finally`块实现。注意,,在相同的相应的`try`块中,您可以使用一个`except`子句以及 `finally`块。如果你想同时使用,你将不得不将一个嵌入到另一个中。
保存为`finally.py`:
~~~python
import time
try:
f = open('poem.txt')
while True: # 通常的读文件
line = f.readline()
if len(line) == 0:
break
print(line, end='')
time.sleep(2) # 确保它运行了一会儿
except KeyboardInterrupt:
print('!! 你取消了从文件读取操作。')
finally:
f.close()
print('(清理:关闭文件)')
~~~
Output:
~~~
$ python3 finally.py
当工作完成时
编程是有趣的
如果想让你的工作有趣
使用Python!
(清理:关闭文件)
~~~
它是如何工作的:
我们做了通常的文件读取工作,但是为了程序缓慢地运行(Python在自然状况下运行非常快)我们武断地使用`time.sleep`函数在每打印完一行后延迟2秒。当程序仍在运行时,,按`ctrl-c`可以中断/取消该程序的运行。
观察到 `KeyboardInterrupt`异常发生程序退出。然而,在程序退出前,最后子句被执行,文件对象总是被关闭。
## with语句
在`try`块中获取资源和随后的在`finally`块释放释放资源是一种常见的模式。因此,这还有一个`with`语句,它使这一切以一个干净的方式运行:
保存为 `using_with.py`:
~~~python
with open("poem.txt") as f:
for line in f:
print(line, end='')
~~~
它是如何工作的:
输出应该与前面的示例相同,不同的是,我们使用的是带有`with`语句的`open`函数 --我们我们把关闭文件的工作留给`with open`自动完成。
在幕后所发生的是,使用`with`语句有一个协议。它通过`open`语句获取返回的对象,在这种情况下我们称之为"thefile"。
在开始代码块时,它*通常*称为 `thefile.__enter__`,代码块执行完毕时,*通常*称为`thefile.__exit__`。
所以,在`finally`块中我们要写的代码将被 `__exit__`方法自动小心,这有利避免重复显式使用 `try..finally`。
这一主题的更多讨论已超本书范围,更多的解释请参考[PEP 343](http://www.python.org/dev/peps/pep-0343/)
## 小结
我们已经讨论了`try..except`和`try..finally`语句的使用方法。我们已经看到了如何创建我们自己的异常类型和如何提出异常。
接下来,我们将探索Python标准库。