在本文中我会介绍关于在Python中如何处理各种错误异常。首先我们来看一下我们在上一篇中拟定的学习计划,加粗体代表我们已经介绍的内容,斜体代表新增内容。
异常处理
文件的读写
正则表达式
操作文件和目录
深拷贝和浅拷贝
面向对象的编程
变量(扩展介绍)
二进制、八进制和十六进制(包括ASCII)
迭代、生成和递归
高阶函数map
匿名函数lambda
图像处理(运用numpy)
2. 异常类型
异常有各种各样,比如我们输入
a = 1 / 0
肯定是会报错的,0不能作为除数,还比如:
a = [0, 1, 2, 3, 4, ]
a[100]
也肯定会报错,因为这个数列压根就没有第100位。
报错的时候,我们通常会看到PyCharm下面运行区内红红的一堆字提示错误。
我们能见到的异常类型有各种各样,
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- StopAsyncIteration
+-- ArithmeticError
| +-- FloatingPointError
| +-- OverflowError
| +-- ZeroDivisionError
+-- AssertionError
+-- AttributeError
+-- BufferError
+-- EOFError
+-- ImportError
| +-- ModuleNotFoundError
+-- LookupError
| +-- IndexError
| +-- KeyError
+-- MemoryError
+-- NameError
| +-- UnboundLocalError
+-- OSError
| +-- BlockingIOError
| +-- ChildProcessError
| +-- ConnectionError
| | +-- BrokenPipeError
| | +-- ConnectionAbortedError
| | +-- ConnectionRefusedError
| | +-- ConnectionResetError
| +-- FileExistsError
| +-- FileNotFoundError
| +-- InterruptedError
| +-- IsADirectoryError
| +-- NotADirectoryError
| +-- PermissionError
| +-- ProcessLookupError
| +-- TimeoutError
+-- ReferenceError
+-- RuntimeError
| +-- NotImplementedError
| +-- RecursionError
+-- SyntaxError
| +-- IndentationError
| +-- TabError
+-- SystemError
+-- TypeError
+-- ValueError
| +-- UnicodeError
| +-- UnicodeDecodeError
| +-- UnicodeEncodeError
| +-- UnicodeTranslateError
+-- Warning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+-- FutureWarning
+-- ImportWarning
+-- UnicodeWarning
+-- BytesWarning
+-- ResourceWarning
这些都是在官网文档上复制粘贴下来的,这里面的比如NameError,指的是我们用了没有定义的变量名,FileNotFoundError指的是要读取的文件不存在,ZeroDivisionError指的是我们除以了一个零。大家可以点击下面的链接来了解各种错误异常。
3. 处理异常
如果我们的代码有错,程序就会在遇见第一个错误时报错并停止运行,为了让程序能够平稳运行,不被错误所打扰,这里我们需要知道如何处理:
try:
# Your code
except:
# What to do with the exception/error
我们需要在我们可能产生错误的代码外面套上try…except语句。try下面直接写上我们原先的代码,except下面写上我们如果遇到程序异常后该怎么做,这样的话我们程序就能遇到错误后依旧平稳运行。
while True:
try:
x = int(input('Please enter a number: '))
break
except:
print('That was no valid number.')
我们运行这段代码,我们可以看到如果我们输入不是整数的话,就会输出except下面的那句话,但是程序并没有“异常终止”,如果我们输入的是整数的话,程序就会安静地运行结束。
可以看到,错误产生时while循环并未被break打断,可以得知异常被捕捉到后马上就会忽略之后的内容,并且直接跳至except。
当然我们可以更清晰一点:
while True:
try:
x = int(input('Please enter a number: '))
break
except ValueError:
print('That was no valid number.')
因为int后面的不能转换为整数时会归为ValueError。如果我们不知道是何种异常时,我们操作方法可以是:
只写except,
运行没有try…except的语句,故意输入错误,看红字显示是什么再回来改
用Exception代替所有可能性,如下
while True:
try:
x = int(input('Please enter a number: '))
break
except Exception:
print('That was no valid number.')
当然,你也可以用BaseException,这里要知道所有的基本错误都归于BaseException下,且绝大部分错误是归于Exception下的。
4. 多种异常
当然,我们的代码有时候会因各种原因碰见多种异常,比如
while True:
try:
x = int(input('Please enter a number: '))
print(100 / x)
break
except Exception:
print('Some error occurred.')
如果用户输入非整数,就会跑except去,如果用户输入是0,0不能做除数,也会跑except去。这是我们可以这样:
while True:
try:
x = int(input('Please enter a number: '))
print(100 / x)
break
except ValueError:
print('That was no valid number.')
except ZeroDivisionError:
print('100 / 0 is invalid.')
这样我们就能在运行时区分到底错在哪里了。当然由于try发现异常后会去找第一个能处理此异常的except,在这里两个except不相关,能交换位置。
而下面的代码:
while True:
try:
x = int(input('Please enter a number: '))
print(100 / x)
break
except Exception:
print('Exception!')
except ValueError:
print('That was no valid number.')
except ZeroDivisionError:
print('100 / 0 is invalid.')
这里它只会看第一个except,发现错误后永远只会输出Exception,原因是Exception包含了ValueError和ZeroDivisionError,try发现异常后看到第一句except能处理,就把异常交给第一个except,自动忽略后面的。
我们还可以如此处理多种异常:
while True:
try:
x = int(input('Please enter a number: '))
print(100 / x)
break
except (ZeroDivisionError, ValueError):
print('Exception!')
用括号括起来,里面能写几个写几个,这样的话我们如果遇见括号里面任意一种异常,就能运行此except。
5. 异常所携带的信息
通常异常中会带有信息,我们可以赋予异常一个变量名以输出具体信息:
while True:
try:
x = int(input('Please enter a number: '))
print(100 / x)
break
except (ZeroDivisionError, ValueError) as e:
print(e)
当然不一定非得命名为e,如果我们输入0,则会输出:
division by zero
而如果我们输入非整数,而是其他字符的话,比如我输入了apple,则会输出:
invalid literal for int() with base 10: 'apple'
这些都是这写异常所携带的信息。
6. finally和else语句
while True:
try:
x = int(input('Please enter a number: '))
print(100 / x)
break
except (ZeroDivisionError, ValueError) as e:
print(e)
finally:
print('Goodbye!')
也就是说不管有没有错,在运行完上述的代码后,一定会执行finally内部的代码。如果我们输入的是0或者非整数,会输出错误信息后带着输出Goodbye然后继续循环会try,如果我们输入正确,运行没有错,while True被break顺利终止了,还是会继续finally内部的代码,输出Goodbye。
while True:
try:
x = int(input('Please enter a number: '))
print(100 / x)
except (ZeroDivisionError, ValueError) as e:
print(e)
else:
print('Goodbye!')
break
else语句只有在try下面没有错的情况下才会运行,注意我把break移到else下面了,我们可以把它看作是try内部内容的延伸。
7. raise语句
当然,我们在自己写码的时候,也会需要在某些场合输出异常。比如我们认为负整数不是整数,那么输入后,我们可以手动输出此异常。
while True:
try:
x = int(input('Please enter a number: '))
if x < 0:
raise ValueError
break
except ValueError as e:
print(e)
但是由于这个ValueError是我们自己输出的,这么写我们并不会看到其携带信息,我们可以这么做让其携带自定义信息:
while True:
try:
x = int(input('Please enter a number: '))
if x < 0:
raise ValueError('Negative number is not allowed')
break
except ValueError as e:
print(e)
输出测试:
Please enter a number: y
invalid literal for int() with base 10: 'y'
Please enter a number: -9
Negative number is not allowed
Please enter a number: 0
输入0,没有错误,程序顺利结束。