Skip to content

Commit cd95de2

Browse files
committed
add Exception
1 parent b265b9e commit cd95de2

1 file changed

Lines changed: 315 additions & 1 deletion

File tree

Exception/README.md

Lines changed: 315 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,316 @@
1-
# 错误和异常
1+
# 异常处理
2+
3+
我们在编写程序的时候,经常需要对异常情况做处理。比如,当一个数试图除以 0 时,我们需要捕获这个异常情况并做处理。你可能会使用类似 if/else 的条件语句来对异常情况做判断,比如,判断除法的分母是否为零,如果为零,则打印错误信息。
4+
5+
这在某些简单的情况下是可以的,但是,在大多数时候,我们应该使用 Python 的**异常处理**机制。这主要有两方面的好处:
6+
7+
- 一方面,你可以选择忽略某些不重要的异常事件,或在需要的时候自己引发异常;
8+
- 另一方面,异常处理不会搞乱原来的代码逻辑,但如果使用一大堆 if/else 语句,不仅会没效率和不灵活,而且会让代码相当难读;
9+
10+
# 异常对象
11+
12+
Python 用**异常对象(exception object)**来表示异常情况。当程序在运行过程中遇到错误时,会引发异常。如果异常对象未被处理或捕捉,程序就会用所谓的回溯(Traceback,一种错误信息)终止执行。
13+
14+
比如:
15+
16+
```python
17+
>>> 1/0
18+
Traceback (most recent call last):
19+
File "<stdin>", line 1, in <module>
20+
ZeroDivisionError: integer division or modulo by zero
21+
```
22+
23+
上面的 `ZeroDivisionError` 就是一个异常类,相应的异常对象就是该类的实例。Python 中所有的异常类都是从 `BaseException` 类派生的,常见的异常类型可以在[这里][1]查看。
24+
25+
# 使用 try/except 捕捉异常
26+
27+
在编写程序的时候,如果我们知道某段代码可能会导致某种异常,而又不希望程序以堆栈跟踪的形式终止,这时我们可以根据需要添加 `try/except` 或者 `try/finally` 语句(或者它们的组合)进行处理。一般来说,有以下使用形式:
28+
29+
```
30+
try...except...
31+
try...except...else...
32+
try...except...else...finally...
33+
try...except...except...else...finally...
34+
try...finally...
35+
```
36+
37+
## 基本形式
38+
39+
捕捉异常的基本形式是 `try...except`
40+
41+
让我们看看第一个例子:
42+
43+
```python
44+
try:
45+
x = input('Enter x: ')
46+
y = input('Enter y: ')
47+
print x / y
48+
except ZeroDivisionError as e:
49+
print 'Error:',e
50+
51+
print 'hello world'
52+
```
53+
54+
当 y = 0 时,看看执行结果:
55+
56+
```python
57+
Enter x: 3
58+
Enter y: 0
59+
Error: integer division or modulo by zero
60+
hello world
61+
```
62+
63+
可以看到,我们的程序正确捕获了`除以零`的异常,而且程序没有以堆栈跟踪的形式终止,而是继续执行后面的代码,打印出 'hello world'。
64+
65+
## 多个 except 子句
66+
67+
有时,我们的程序可能会出现多个异常,这时可以用多个 except 子句来处理这种情况。
68+
69+
让我们继续看第一个例子,如果 y 输入的是一个非数字的值,就会产生另外一个异常:
70+
71+
```python
72+
Enter x: 2
73+
Enter y: 'a' # y 的输入是一个字符
74+
----------------------------------------------------------------------
75+
TypeError Traceback (most recent call last)
76+
<ipython-input-209-d4666cfaefb4> in <module>()
77+
2 x = input('Enter x: ')
78+
3 y = input('Enter y: ')
79+
----> 4 print x / y
80+
5 except ZeroDivisionError as e:
81+
6 print e
82+
83+
TypeError: unsupported operand type(s) for /: 'int' and 'str'
84+
```
85+
86+
可以看到,当 y 输入一个字符 'a' 之后,程序产生了一个 `TypeError` 异常,并且终止,这是因为我们的 except 子句只是捕获了 `ZeroDivisionError` 异常,为了能捕获`TypeError` 异常,我们可以再加一个 except 子句,完整代码如下:
87+
88+
```python
89+
try:
90+
x = input('Enter x: ')
91+
y = input('Enter y: ')
92+
print x / y
93+
except ZeroDivisionError as e: # 处理 ZeroDivisionError 异常
94+
print 'ZeroDivisionError:',e
95+
except TypeError as e: # 处理 TypeError 异常
96+
print 'TypeError:',e
97+
98+
print 'hello world'
99+
```
100+
101+
当 y 输入 'a' 时,看看执行结果:
102+
103+
```python
104+
Enter x: 3
105+
Enter y: 'a'
106+
TypeError: unsupported operand type(s) for /: 'int' and 'str'
107+
hello world
108+
```
109+
110+
## 捕捉未知异常
111+
112+
事实上,在编写程序的时候,我们很难预料到程序的所有异常情况。比如,对于第一个例子,我们可以预料到一个 `ZeroDivisionError` 异常,如果细心一点,也会预料到一个 `TypeError` 异常,可是,还是有一些其他情况我们没有考虑到,比如在输入 x 的时候,我们直接按回车键,这时又会引发一个异常,程序也会随之挂掉:
113+
114+
```python
115+
Enter x: # 这里输入回车键
116+
Traceback (most recent call last):
117+
File "<stdin>", line 2, in <module>
118+
File "<string>", line 0
119+
120+
^
121+
SyntaxError: unexpected EOF while parsing
122+
```
123+
124+
那么,我们应该怎么在程序捕获某些难以预料的异常呢?我们在上文说过,Python 中所有的异常类都是从 `BaseException` 类派生的,也就是说,`ZeroDivisionError``SyntaxError` 等都是它的子类,因此,对于某些难以预料的异常,我们就可以使用 `BaseException` 来捕获,在大部分情况下,我们也可以使用 `Exception` 来捕获,因为 `Exception` 是大部分异常的父类,可以到[这里][1]查看所有异常类的继承关系。
125+
126+
因此,对于第一个例子,我们可以把程序做一些修改,使其更加健壮:
127+
128+
```python
129+
try:
130+
x = input('Enter x: ')
131+
y = input('Enter y: ')
132+
print x / y
133+
except ZeroDivisionError as e: # 捕获 ZeroDivisionError 异常
134+
print 'ZeroDivisionError:',e
135+
except TypeError as e: # 捕获 TypeError 异常
136+
print 'TypeError:',e
137+
except BaseException as e: # 捕获其他异常
138+
print 'BaseException:',e
139+
140+
print 'hello world'
141+
```
142+
143+
注意到,我们把 `BaseException` 写在了最后一个 except 子句。如果你把它写在了第一个 except 子句,由于 `BaseException` 是所有异常的父类,那么程序的所有异常都会被第一个 except 子句捕获。
144+
145+
## else 子句
146+
147+
我们可以在 except 子句后面加一个 else 子句。当没有异常发生时,会自动执行 else 子句。
148+
149+
对第一个例子,加入 else 子句:
150+
151+
```python
152+
try:
153+
x = input('Enter x: ')
154+
y = input('Enter y: ')
155+
print x / y
156+
except ZeroDivisionError as e:
157+
print 'ZeroDivisionError:',e
158+
except TypeError as e:
159+
print 'TypeError:',e
160+
except BaseException as e:
161+
print 'BaseException:',e
162+
else:
163+
print 'no error!'
164+
165+
print 'hello world'
166+
```
167+
168+
看看执行结果:
169+
170+
```
171+
Enter x: 6
172+
Enter y: 2
173+
3
174+
no error!
175+
hello world
176+
```
177+
178+
## finally 子句
179+
180+
finally 子句不管有没有出现异常都会被执行。
181+
182+
看看例子:
183+
184+
```python
185+
try:
186+
x = 1/0
187+
print x
188+
finally:
189+
print 'DONE'
190+
```
191+
192+
执行结果:
193+
194+
```
195+
DONE
196+
Traceback (most recent call last):
197+
File "<stdin>", line 2, in <module>
198+
ZeroDivisionError: integer division or modulo by zero
199+
```
200+
201+
再看一个例子:
202+
203+
```python
204+
try:
205+
x = 1/0
206+
print x
207+
except ZeroDivisionError as e:
208+
print 'ZeroDivisionError:',e
209+
finally:
210+
print 'DONE'
211+
```
212+
213+
执行结果:
214+
215+
```
216+
ZeroDivisionError: integer division or modulo by zero
217+
DONE
218+
```
219+
220+
## 使用 raise 手动引发异常
221+
222+
有时,我们使用 except 捕获了异常,又想把异常抛出去,这时可以使用 raise 语句。
223+
224+
看看例子:
225+
226+
```python
227+
try:
228+
x = input('Enter x: ')
229+
y = input('Enter y: ')
230+
print x / y
231+
except ZeroDivisionError as e:
232+
print 'ZeroDivisionError:',e
233+
except TypeError as e:
234+
print 'TypeError:',e
235+
except BaseException as e:
236+
print 'BaseException:',e
237+
raise # 使用 raise 抛出异常
238+
else:
239+
print 'no error!'
240+
241+
print 'hello world'
242+
```
243+
244+
运行上面代码,当 x 输入一个回车键时,错误会被打印出来,并被抛出:
245+
246+
```python
247+
Enter x: # 这里输入回车键
248+
BaseException: unexpected EOF while parsing (<string>, line 0)
249+
Traceback (most recent call last):
250+
File "<stdin>", line 2, in <module>
251+
File "<string>", line 0
252+
253+
^
254+
SyntaxError: unexpected EOF while parsing
255+
```
256+
257+
上面的 raise 语句是不带参数的,它会把当前错误原样抛出。事实上,我们也创建自己的异常类,并抛出自定义的异常。
258+
259+
**创建自定义的异常类需要从 Exception 类继承**,可以间接继承或直接继承,也就是可以继承其他的内建异常类。比如:
260+
261+
```python
262+
# 自定义异常类
263+
class SomeError(Exception):
264+
pass
265+
266+
try:
267+
x = input('Enter x: ')
268+
y = input('Enter y: ')
269+
print x / y
270+
except ZeroDivisionError as e:
271+
print 'ZeroDivisionError:',e
272+
except TypeError as e:
273+
print 'TypeError:',e
274+
except BaseException as e:
275+
print 'BaseException:',e
276+
raise SomeError('invalid value') # 抛出自定义的异常
277+
else:
278+
print 'no error!'
279+
280+
print 'hello world'
281+
```
282+
283+
运行上面代码,当 x 输入一个回车键时,错误被打印出来,并抛出我们自定义的异常:
284+
285+
```
286+
Enter x:
287+
BaseException: unexpected EOF while parsing (<string>, line 0)
288+
----------------------------------------------------------------------
289+
SomeError Traceback (most recent call last)
290+
<ipython-input-20-66060b472f91> in <module>()
291+
12 except BaseException as e:
292+
13 print 'BaseException:',e
293+
---> 14 raise SomeError('invalid value')
294+
15 else:
295+
16 print 'no error!'
296+
297+
SomeError: invalid value
298+
```
299+
300+
# 小结
301+
302+
- Python 中所有的异常类都是从 `BaseException` 类派生的。
303+
- 通过 try/except 来捕捉异常,可以使用多个 except 子句来分别处理不同的异常。
304+
- else 子句在主 try 块没有引发异常的情况下被执行。
305+
- finally 子句不管是否发生异常都会被执行。
306+
- 通过继承 Exception 类可以创建自己的异常类。
307+
308+
# 参考资料
309+
310+
- 《Python 基础教程》
311+
- [Python 异常处理](http://www.runoob.com/python/python-exceptions.html)
312+
313+
314+
[1]: https://docs.python.org/2/library/exceptions.html#exception-hierarchy
315+
2316

0 commit comments

Comments
 (0)