Files
ailearning/docs/da/025.md
2020-10-19 21:08:55 +08:00

521 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 异常
## try & except 块
写代码的时候,出现错误必不可免,即使代码没有问题,也可能遇到别的问题。
看下面这段代码:
```py
import math
while True:
text = raw_input('> ')
if text[0] == 'q':
break
x = float(text)
y = math.log10(x)
print "log10({0}) = {1}".format(x, y)
```
这段代码接收命令行的输入,当输入为数字时,计算它的对数并输出,直到输入值为 `q` 为止。
乍看没什么问题然而当我们输入0或者负数时
In [1]:
```py
import math
while True:
text = raw_input('> ')
if text[0] == 'q':
break
x = float(text)
y = math.log10(x)
print "log10({0}) = {1}".format(x, y)
```
```py
> -1
```
```py
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-1-ceb8cf66641b> in <module>()
6 break
7 x = float(text)
----> 8 y = math.log10(x)
9 print "log10({0}) = {1}".format(x, y)
ValueError: math domain error
```
`log10` 函数会报错,因为不能接受非正值。
一旦报错,程序就会停止执行,如果不希望程序停止执行,那么我们可以添加一对 `try & except`
```py
import math
while True:
try:
text = raw_input('> ')
if text[0] == 'q':
break
x = float(text)
y = math.log10(x)
print "log10({0}) = {1}".format(x, y)
except ValueError:
print "the value must be greater than 0"
```
一旦 `try` 块中的内容出现了异常,那么 `try` 块后面的内容会被忽略,**Python**会寻找 `except` 里面有没有对应的内容,如果找到,就执行对应的块,没有则抛出这个异常。
在上面的例子中,`try` 抛出的是 `ValueError``except` 中有对应的内容,所以这个异常被 `except` 捕捉到,程序可以继续执行:
In [2]:
```py
import math
while True:
try:
text = raw_input('> ')
if text[0] == 'q':
break
x = float(text)
y = math.log10(x)
print "log10({0}) = {1}".format(x, y)
except ValueError:
print "the value must be greater than 0"
```
```py
> -1
the value must be greater than 0
> 0
the value must be greater than 0
> 1
log10(1.0) = 0.0
> q
```
## 捕捉不同的错误类型
```py
import math
while True:
try:
text = raw_input('> ')
if text[0] == 'q':
break
x = float(text)
y = 1 / math.log10(x)
print "log10({0}) = {1}".format(x, y)
except ValueError:
print "the value must be greater than 0"
```
假设我们将这里的 `y` 更改为 `1 / math.log10(x)`,此时输入 `1`
In [3]:
```py
import math
while True:
try:
text = raw_input('> ')
if text[0] == 'q':
break
x = float(text)
y = 1 / math.log10(x)
print "log10({0}) = {1}".format(x, y)
except ValueError:
print "the value must be greater than 0"
```
```py
> 1
```
```py
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
<ipython-input-3-7607f1ae6af9> in <module>()
7 break
8 x = float(text)
----> 9 y = 1 / math.log10(x)
10 print "log10({0}) = {1}".format(x, y)
11 except ValueError:
ZeroDivisionError: float division by zero
```
因为我们的 `except` 里面并没有 `ZeroDivisionError`,所以会抛出这个异常,我们可以通过两种方式解决这个问题:
## 捕捉所有异常
`except` 的值改成 `Exception` 类,来捕获所有的异常。
In [4]:
```py
import math
while True:
try:
text = raw_input('> ')
if text[0] == 'q':
break
x = float(text)
y = 1 / math.log10(x)
print "1 / log10({0}) = {1}".format(x, y)
except Exception:
print "invalid value"
```
```py
> 1
invalid value
> 0
invalid value
> -1
invalid value
> 2
1 / log10(2.0) = 3.32192809489
> q
```
## 指定特定值
这里,我们把 `ZeroDivisionError` 加入 `except`
In [5]:
```py
import math
while True:
try:
text = raw_input('> ')
if text[0] == 'q':
break
x = float(text)
y = 1 / math.log10(x)
print "1 / log10({0}) = {1}".format(x, y)
except (ValueError, ZeroDivisionError):
print "invalid value"
```
```py
> 1
invalid value
> -1
invalid value
> 0
invalid value
> q
```
或者另加处理:
In [6]:
```py
import math
while True:
try:
text = raw_input('> ')
if text[0] == 'q':
break
x = float(text)
y = 1 / math.log10(x)
print "1 / log10({0}) = {1}".format(x, y)
except ValueError:
print "the value must be greater than 0"
except ZeroDivisionError:
print "the value must not be 1"
```
```py
> 1
the value must not be 1
> -1
the value must be greater than 0
> 0
the value must be greater than 0
> 2
1 / log10(2.0) = 3.32192809489
> q
```
事实上,我们还可以将这两种方式结合起来,用 `Exception` 来捕捉其他的错误:
In [7]:
```py
import math
while True:
try:
text = raw_input('> ')
if text[0] == 'q':
break
x = float(text)
y = 1 / math.log10(x)
print "1 / log10({0}) = {1}".format(x, y)
except ValueError:
print "the value must be greater than 0"
except ZeroDivisionError:
print "the value must not be 1"
except Exception:
print "unexpected error"
```
```py
> 1
the value must not be 1
> -1
the value must be greater than 0
> 0
the value must be greater than 0
> q
```
## 得到异常的具体信息
在上面的例子中,当我们输入不能转换为浮点数的字符串时,它输出的是 `the value must be greater than 0`,这并没有反映出实际情况。
In [8]:
```py
float('a')
```
```py
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-8-99859da4e72c> in <module>()
----> 1 float('a')
ValueError: could not convert string to float: a
```
为了得到异常的具体信息,我们将这个 `ValueError` 具现化:
In [9]:
```py
import math
while True:
try:
text = raw_input('> ')
if text[0] == 'q':
break
x = float(text)
y = 1 / math.log10(x)
print "1 / log10({0}) = {1}".format(x, y)
except ValueError as exc:
if exc.message == "math domain error":
print "the value must be greater than 0"
else:
print "could not convert '%s' to float" % text
except ZeroDivisionError:
print "the value must not be 1"
except Exception as exc:
print "unexpected error:", exc.message
```
```py
> 1
the value must not be 1
> -1
the value must be greater than 0
> aa
could not convert 'aa' to float
> q
```
同时,我们也将捕获的其他异常的信息显示出来。
这里,`exc.message` 显示的内容是异常对应的说明,例如
```py
ValueError: could not convert string to float: a
```
对应的 `message`
```py
could not convert string to float: a
```
当我们使用 `except Exception` 时,会捕获所有的 `Exception` 和它派生出来的子类,但不是所有的异常都是从 `Exception` 类派生出来的,可能会出现一些不能捕获的情况,因此,更加一般的做法是使用这样的形式:
```py
try:
pass
except:
pass
```
这样不指定异常的类型会捕获所有的异常,但是这样的形式并不推荐。
## 自定义异常
异常是标准库中的类,这意味着我们可以自定义异常类:
In [10]:
```py
class CommandError(ValueError):
pass
```
这里我们定义了一个继承自 `ValueError` 的异常类,异常类一般接收一个字符串作为输入,并把这个字符串当作异常信息,例如:
In [11]:
```py
valid_commands = {'start', 'stop', 'pause'}
while True:
command = raw_input('> ')
if command.lower() not in valid_commands:
raise CommandError('Invalid commmand: %s' % command)
```
```py
> bad command
```
```py
---------------------------------------------------------------------------
CommandError Traceback (most recent call last)
<ipython-input-11-0e1f81a1136d> in <module>()
4 command = raw_input('> ')
5 if command.lower() not in valid_commands:
----> 6 raise CommandError('Invalid commmand: %s' % command)
CommandError: Invalid commmand: bad command
```
我们使用 `raise` 关键词来抛出异常。
我们可以使用 `try/except` 块来捕捉这个异常:
```py
valid_commands = {'start', 'stop', 'pause'}
while True:
command = raw_input('> ')
try:
if command.lower() not in valid_commands:
raise CommandError('Invalid commmand: %s' % command)
except CommandError:
print 'Bad command string: "%s"' % command
```
由于 `CommandError` 继承自 `ValueError`,我们也可以使用 `except ValueError` 来捕获这个异常。
## finally
try/catch 块还有一个可选的关键词 finally。
不管 try 块有没有异常, finally 块的内容总是会被执行,而且会在抛出异常前执行,因此可以用来作为安全保证,比如确保打开的文件被关闭。。
In [12]:
```py
try:
print 1
finally:
print 'finally was called.'
```
```py
1
finally was called.
```
在抛出异常前执行:
In [13]:
```py
try:
print 1 / 0
finally:
print 'finally was called.'
```
```py
finally was called.
```
```py
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
<ipython-input-13-87ecdf8b9265> in <module>()
1 try:
----> 2 print 1 / 0
3 finally:
4 print 'finally was called.'
ZeroDivisionError: integer division or modulo by zero
```
如果异常被捕获了,在最后执行:
In [14]:
```py
try:
print 1 / 0
except ZeroDivisionError:
print 'divide by 0.'
finally:
print 'finally was called.'
```
```py
divide by 0.
finally was called.
```