在Python中优雅处理错误的最佳实践
最大的错误是想象我们永远不会犯错 - 托马斯·卡莱尔
在开发可维护且健壮的代码时,添加全面的错误处理至关重要。
错误可以分为几类:逻辑错误、生成错误、编译时错误和运行时错误。在本文中,我们将专注于处理运行时错误 - 程序运行时发生的错误。
为什么我们需要处理错误?
- 避免程序错误发生时崩溃
- 如果程序发生了错误,我们不希望程序在用户上崩溃。相反,错误处理可用于通知用户错误发生的原因并优雅地退出导致错误的进程。
- 节省调试错误的时间
- 参考原因1,让程序显示错误而不是立即崩溃将在调试错误时节省大量的时间。
- 可以更新错误处理程序内部的逻辑来显示对开发人员有用的信息,例如代码回溯、错误类型等。
- 帮助定义程序的要求
- 如果程序由于错误输入而崩溃,错误处理程序可以通知用户错误发生的原因并定义程序的要求和约束。
错误处理实践
在本文中,我们将讨论以下内容:
- 使用
try - except和try - except - finally处理异常 - 断言
- 何时使用异常(Exception)和断言(Assertions)
捕获和处理异常
如果你有一个可能会失败的代码块,你可以将这段代码放在try ... except块中来管理异常。
try:
x = int(input("Please enter a number: "))
y = 100 / x
except ValueError:
print("Error: there was an error")
except ZeroDivisionError:
print("Error: 0 is an invalid number")
except Exception:
print("Error: another error occurred")
finally:
print("Cleanup can go here")
错误处理执行如下:
- 执行
try块内的语句。 - 如果语句成功,则跳过两个
except子句并运行finally子句中的代码。 - 如果
try块中的语句失败,则执行第一个except语句中的代码。如果语句由于ValueError(即无法将非数字转换为int)而失败,则运行except ValueError块中的代码。 - 如果
try块中的语句失败并且错误不是ValueError,则检查第二个except语句。 如果语句由于ZeroDivisionError(即整数被零除)而失败,则运行except ZeroDivisionError块中的代码。 finally子句将始终在最后一个任务完成后执行——无论最后一个任务是在try块中还是在except块中。
处理异常时要注意的事项
- 不要让代码吞下异常。我们不希望通过简单地忽略错误而未被发现。如果您需要吞下异常以避免根本问题,则需要重新评估程序的架构。
try:
y = 100 / x
except ZeroDivisionError:
pass
- 不要在 try 语句中声明可能无法访问到的新变量。
try:
y = 100 / x
z = 23 * y
except ZeroDivisionError:
pass
print(z) # z will be undeclared if an Exception is raised
抛出异常
您可能想要重新引发异常以中止脚本。例如,如果我们无法确定导致异常的错误类型,我们可能需要重新抛出它:
try:
x = int(input("Please enter a number: "))
y = 100 / x
except ValueError:
print("Error: there was an error")
except ZeroDivisionError:
print("Error: 0 is an invalid number")
except Exception:
raise
用户定义的异常
有几种类型的内置异常类继承自相同的基本异常类。这些内置类的完整列表可以在官方文档中找到。
也可以创建一个继承自 Exception 基类的自定义异常类。如果开发人员希望集成更复杂的日志系统或进一步检查对象,则可能需要自定义类。
定义 Exception 类时需要 init() 和 str() 方法:
class CustomError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return "Error: %s" % self.value
try:
raise CustomError("something went wrong")
except CustomError as e:
print(e)
# prints "Error: something went wrong"
断言
断言将表达式计算为true或false。如果表达式为 false,python 将引发 AssertionError 异常。在测试代码时,断言可以作为强大的开发人员工具。
断言的语法是 assert Expression[, Arguments]:
a = 20
assert a < 10, "something went wrong"
上面的代码会抛出这个错误:
Traceback (most recent call last):
File "file.py", line 2, in <module>
assert a < 10, "something went wrong"
AssertionError: something went wrong
那么我们什么时候应该使用断言和异常呢?
这真的归结为个案的基础,这里有辩论的余地。在我看来,由于用户输入、硬件、网络等原因,在处理外部输入和输出时应该使用异常。当您想正常退出程序、记录数据并通知用户发生此类错误的原因时,应使用异常。
断言具有快速失败的方法,应该用于查找代码中的错误和检测错误。如果您的生产代码中有断言,我的建议是确保设置异常处理以捕获任何断言错误。如果断言在生产中失败,至少代码将通过理想地退出程序、记录问题并通知用户来安全地处理异常。