今天我们来说说Python的异常处理,在说异常处理之前我们先来了解下,Python中有哪些常见的错误类型。
在Python中,当代码中有错误时,程序默认会在终端中将错误类型和原因打印出来:
以下是Python中一些常用错误类型:
- SyntaxError(语法错误)
print('函数最后没有加小括号'
#运行程序后报错 SyntaxError: unexpected EOF while parsing
- NameError(名称错误)
if not_defined > 0:
print("NOk")
#运行程序后报错 NameError: name 'not_defined' is not defined
- LookupError (查询错误)
li = [1, 2, 3]
li[1024]
#运行程序后报错 IndexError: list index out of range
di = {'code':'JD', 'price':42}
di['CEO']
#运行程序后报错 KeyError: 'CEO'
- ArithmeticError(运算错误)
1/0
#运行程序后报错 ZeroDivisionError: division by zero
- TypeError(类型错误)
days = 365 + '1004'
#运行程序后报错 TypeError: unsupported operand type(s) for +: 'int' and 'str'
- ValueError(值错误)
int('永远有多远')
#运行程序后报错 ValueError: invalid literal for int() with base 10: '永远有多远'
- AttributeError(属性错误)
a = 1
a.append(2)
#运行程序后报错 AttributeError: 'int' object has no attribute 'append'
- OSError(系统错误)
f = open('No Such File.txt')
#运行程序后报错 FileNotFoundError: [Errno 2] No such file or directory: 'No Such File.txt'
- ImportError(导入错误)
from numpy import sqr
#运行程序后报错 ImportError: cannot import name 'sqr' from 'numpy'
- ModuleNotFoundError(未找到模块错误)
import numq
#运行程序后报错 ModuleNotFoundError: No module named 'numq'
- StopIteration(迭代停止错误)
i = iter([1,2,3])
print(next(i))
print(next(i))
print(next(i))
print(next(i))
#运行程序后报错 StopIteration:
看到这里,你一定会有一个疑问,Python怎么有那么多不同的错误分类呀,我都需要一一全都记下来吗?
就个人学习的经验来说,可以先以了解为主,在后续实际应用中,记住与自己代码相关的错误类型即可。
随后,我们来说说今天的主题,异常处理,我们首先需要明白的是,我们无法完全阻止错误发生,但是可以提前预防以至于程序不会崩溃。这个提前预防的动作称为异常处理(exception handling),异常处理就是为了防患于未然。
从以上的错误类型我们知道,Python 在程序报错时会返回错误类型和信息,我们可以提前去捕捉我们能预计到的及无法预计到的错误类型,并做相应的处理。
接下来,我们来介绍几种典型的异常处理方式:
1. Try-Except
异常处理最常见的语句就是 try-except 组合,细分又有三种类型:
(1) 知道错误但不确定类型,用 except Exception
except Exception
例子:
def divide(a, b):
try:
c = a / b
print(f"Result = {c:.4f}.")
except Exception as err:
print(f'因子是0,无法进行相除! 具体错误为:{err}')
divide(10, 0) #因子是0,无法进行相除! 具体错误为:division by zero
这里解释下:
except Exception as err
的含义是将任意异常定义为一个err对象,后续可将err转为字符串打印出来,程序会打印错误原因,如需同时打印错误类型和原因,则需要使用repr(err)。
(2)知道错误而且确定类型,用 except some_exception
except some_exception
例子:
def divide(a, b):
try:
c = a / b
print(f"Result = {c:.4f}.")
except ZeroDivisionError:
print(f'因子是0,无法进行相除! ')
divide(10, 0) #因子是0,无法进行相除!
(3)知道错误而且有多个错误
用多个 except
或
用 except (exc_1, exc_2, ... exc_n)
多个错误
例子:
def divide(a, b):
try:
c = a / b
d = cc + 1
print(f"Result = {c:.4f}.")
except ZeroDivisionError:
print('因子是0,无法进行相除!')
except NameError:
print('变量名未定义!')
divide(10, 2) #变量名未定义!
divide(10, 0) #因子是0,无法进行相除!
有时,我们也可以对多个错误进行统一处理,比如:
except (ZeroDivisionError,NameError):
print('Error occurred!')
实际工作中,我们也会遇到不同错误类型,做差分处理的情况,比如控制机械手臂时,如遇到sockets连接丢失的异常时,可以做重连操作,遇到控制指令返回超时的异常可以做提示重试的处理,其他任意错误则终止机械手臂的控制,这样代码既智能又安全。
try:
动作
except SocketsError:
重连处理
except NoResponseError:
提示重试处理
except Exception:
停止控制
p.s 以上SocketsError和NoResponseError为自建的错误类型,Exception为所有其他异常即所有其他错误类型,程序出错时会进入对应的一种错误类型中去。
2. Try-Except-Else
首先要明确的是,else 语句是可有可无的。如果存在,则 else 语句应始终在 except 语句之后。
当 try 语句下的代码未发生异常时,才会执行 else 子句下的代码。
当 try 语句下的代码中发生异常,则 except 语句将处理异常,else 语句将不会执行。
个人认为,Try-Except-Else的存在感并不是很强,因为完全可以通过在try的最后,添加相应的代码来实现同样的功能。
3. Try-Except-Else-Finally
无论是否发生异常,finally 语句始终在 try 语句运行之前执行。
在实际工作中,finally 是非常有用的。
比如调用音频采集卡时,我们希望无论采集成功或异常失败时,最终程序都会做一次停止采集的动作来释放资源。这时我们就可以将停止采集的动作放在finally语句中。
4. Raise Exception
除了上面处理异常的操作之外,我们还可以用 raise 关键词“抛出”异常,抛出异常可分为两类:
(1) 抛出内置异常
在下例中,如果输入非整数,我们抛出一个 ValueError(注意这是 Python 里面内置的异常对象),顺带打印“This is not a positive number”的信息。
try:
a = int(input("Enter a positive integer: "))
if a <= 0:
raise ValueError("That is not a positive number!")
except ValueError as err:
print(err)
(2) 抛出自定义异常
这里我们先需要了解如何自定义异常:
在 Python 里,所有异常都是 Exception 的子类,因此在定义其类时需要进行继承。
class Error(Exception):
class your_exception(Error):
具体代码如下。
class Error(Exception):
pass
class NegativePortfolioValueWarning(Error):
def __init__(self, expression, message):
self.expression = expression
self.message = message
随后,我们就可以在代码中,抛出自定义的NegativePortfolioValueWarning异常了。
def portfolio_value(last_worth, current_worth):
if last_worth < 0 or current_worth < 0:
raise ValueError('Negative worth!')
value = current_worth - last_worth
if value < 0:
raise NegativePortfolioValueWarning(
'NegativePortfolioValueWarning', \
'Negative return. Take a look!')
总结,Python中对不同错误类型做了细分且分类非常多,实际代码调试过程中,我们可以通过打印的方式将错误类型和原因打印出来,以便让程序继续运行的前提下了解错误信息。
对于不同的错误类型,我们可以做差分处理,以便让程序容错性更好且更智能。
对于自己编写程序的一些特殊异常。我们可以自建错误类型,并通过raise抛出。
网友评论