try:withopen('data.csv')asfile:
read_data =file.read()except FileNotFoundError as fnf_error:print(fnf_error)print("Explanation: We cannot load the 'data.csv' file")技术支持
[Errno 2] No such fileor directory:'data.csv'
Explanation: We cannot load the 'data.csv'file技术支持
divide 函数用于处理 ZeroDivisionError 异常,并在没有异常时显示结果。无论结果如何,都会运行 finally,以绿色打印 “Code by DataCamp”。
defdivide(x,y):try:
result = x/y
except ZeroDivisionError:print("Please change 'y' argument to non-zero value")except:print("Something went wrong")else:print(f"Your answer is {result}")finally:print("\033[92m Code by DataCamp\033[00m")技术支持
defdivide(x,y):try:
value =50
x.append(value)except AttributeError as atr_err:print(atr_err)else:try:
result =[i / y for i in x]print( result )except ZeroDivisionError:print("Please change 'y' argument to non-zero value")finally:print("\033[92m Code by DataCamp\033[00m")技术支持
第一种场景中,我们提供含四个值的列表 x 和分母 3。脚本会向列表添加 50,将列表中的每个值除以 3,并显示结果。
Please change 'y' argument to non-zero value
Code by DataCamp技术支持
文件编辑示例
我们来看更贴近实际的例子:加载文件、写入文本并关闭文件。
file_editor() 函数将:
检查 open() 函数是否引发 FileNotFoundError。
如果外层未引发异常,则检查 write() 函数的异常。
无论如何,在打开文件后都会通过 finally 语句关闭文件。
如果外层 try 语句引发异常,将返回带有无效文件路径的错误消息。
deffile_editor(path,text):try:
data =open(path)try:
data.write(text)except:print("Unable to write the data. Please add an append: 'a' or write: 'w' parameter to the open() function.")finally:
data.close()except:print(f"{path} file is not found!!")技术支持
第一种场景中,我们提供文件路径与文本。
path ="data.txt"
text ="DataLab: Share your data analysis in a cloud-based environment--no installation required."
file_editor(path,text)技术支持
如下示例中,若值超过 1,000 则抛出 ValueError。我们把值改为 2,000,使 if 条件为 TRUE,并抛出带自定义消息的 ValueError。自定义错误消息有助于快速定位问题。
value =2_000if value >1_000:# raise the ValueErrorraise ValueError("Please add a value lower than 1,000")else:print("Congratulations! You are the winner!!")技术支持
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)---->4raise ValueError("Please add a value lower than 1,000")5else:6print("Congratulations! You are the winner!!")
ValueError: Please add a value lower than 1,000技术支持
if value >1_000:# raise the Exceptionraise Exception("Please add a value lower than 1,000")else:print("Congratulations! You are the winner!!")技术支持
---------------------------------------------------------------------------
Exception Traceback (most recent call last)---->3raise Exception("Please add a value lower than 1,000")4else:5print("Congratulations! You are the winner!!")
Exception: Please add a value lower than 1,000技术支持
处理已抛出的异常示例
我们也可以自定义异常,并用 try-except 来处理。
在此示例中,我们把抛出 ValueError 的逻辑放在 try 语句下。
它如何工作?不是抛出异常并终止程序,而是显示我们提供的错误消息。
value =2_000try:if value >1_000:# raise the ValueErrorraise ValueError("Please add a value lower than 1,000")else:print("Congratulations! You are the winner!!")# if false then raise the value errorexcept ValueError as e:print(e)技术支持
除了单独抛出异常外,还可以使用 raise ... from 进行异常链。这样会保留原始异常作为上下文,使当一个错误引发另一个错误时,回溯信息更清晰。
try:open("config.json")except FileNotFoundError as e:raise RuntimeError("App failed to start: config file missing")from e技术支持
FileNotFoundError:[Errno 2] No such fileor directory:'config.json'
The above exception was the direct cause of the following exception:
RuntimeError: App failed to start: config file missing技术支持
其中 from e 会在新异常上设置 __cause__ 属性,使阅读回溯的人可以明确看到其触发原因。这在大型应用中尤为有用,因为异常会跨越多个代码层级。
Python 3.10 引入了结构化模式匹配,可在 match 语句中按错误类型匹配,从而更优雅地处理异常。对于需要处理多种异常类型、且希望可读性更强的场景尤其有用。例如:
try:
result =1/0except Exception as e:match e:case ZeroDivisionError():print("You cannot divide by zero.")case NameError():print("Variable not defined.")case_:print("An unexpected error occurred.")技术支持
try:raise ExceptionGroup("Multiple errors",[ValueError("Bad value"), TypeError("Bad type")])except* ValueError as ve:print(f"Handling ValueError: {ve}")except* TypeError as te:print(f"Handling TypeError: {te}")技术支持
4. 异常的 add_note 方法(Python 3.11)
异常对象现在具有 add_note() 方法,开发者可以为异常添加自定义说明,便于调试。例如:
try:raise ValueError("Invalid input")except ValueError as e:
e.add_note("This happened while processing user input.")
e.add_note("Consider validating input before processing.")raise技术支持
ValueError: Invalid input
Notes:- This happened while processing user input.- Consider validating input before processing.技术支持
错误和异常可能导致意外行为,甚至让程序停止执行。Python 提供了多种函数和机制来处理这些问题,从而提高代码的健壮性。本文将通过示例学习各种错误类型和内置函数。
虽然异常发生在运行时,但某些错误(如
SyntaxError和IndentationError)会在程序运行前被解析器捕获。简单来说,错误是正常应用不应捕获的致命问题,而异常是程序应该捕获并处理的情况。
接下来通过各种示例进一步了解错误与异常。为便于运行这些示例,您可以免费创建一个 DataLab 工作簿,其中预装了 Python 并包含所有代码样例。
Python 中的错误
下面是一个语法错误示例:函数外部的 return 没有意义。我们不应在程序中处理这类错误,而是要正确定义一个返回字符串的函数。
我们定义了函数,但缩进不正确。缩进错误不应在运行时处理,应该手动修复或使用代码格式化工具。
Python 中的异常
我们遇到了一个
ZeroDivisionError(异常)。可以在运行时使用try和except块来处理。NameError异常很常见,通常是未找到变量。我们可以通过替换变量或打印警告来处理该异常。Python 内置异常
以下是 Python 默认异常及其说明:
AssertionError:当 assert 语句失败时引发。EOFError:当input()函数遇到文件结束条件时引发。AttributeError:当属性赋值或引用失败时引发。TabError:当缩进由不一致的制表符或空格组成时引发。ImportError:当导入模块失败时引发。IndexError:当序列的索引超出范围时发生。KeyboardInterrupt:当用户输入中断键(Ctrl + C 或 Delete)时引发。RuntimeError:当错误不属于任何特定类别时发生。NameError:当在局部或全局作用域中找不到变量时引发。MemoryError:当程序内存耗尽时引发。ValueError:当操作或函数接收到类型正确但值不正确的参数时发生。ZeroDivisionError:当用零去除某个值或变量时引发。SyntaxError:当 Python 语法错误时由解析器引发。IndentationError:当缩进不正确时发生。SystemError:当解释器检测到内部错误时引发。您可以在文档中找到完整的错误与异常列表。
通过我们的 Object-Oriented Programming in Python 课程学习 Python 异常。课程将教您如何创建类,并利用继承与多态来复用与优化代码。
使用 try、except、else 与 finally 进行异常处理
了解错误与异常之后,我们将通过
try、except、else和finally块来学习如何处理它们。所谓“处理”,是什么意思?在常规情况下,这些错误会停止代码执行并显示错误信息。为了构建稳定的系统,我们需要预判这些错误,并提供替代方案或警告信息。
本节将学习每个代码块的作用,以及如何用它们来编写健壮的代码。
The try and except statement
在 Python 中处理异常最简单的方式是使用
try和except块。运行
try语句下的代码。当引发异常时,执行
except语句下的代码。这样,代码不会在错误或异常处停止,而是转入替代方案。
简单示例
第一个示例中,我们尝试打印未定义的变量
x。在常规情况下,这会抛出错误并停止执行,但借助try和except块,我们可以改变控制流。try语句下的代码。x未定义,所以会运行 except 语句并打印警告。多个 except 语句示例
第二个示例中,我们使用多个
except语句来处理多种异常类型。ZeroDivisionError,程序将打印 “You cannot divide a value with zero.”这样就可以编写在不崩溃的情况下同时处理多种异常的灵活代码。
注意:避免使用不带异常类型的裸
except:子句——它会捕获所有内容,包括KeyboardInterrupt和SystemExit,从而可能导致程序无法退出并隐藏无关的缺陷。务必只捕获您预期的特定异常,例如except ZeroDivisionError:。加载文件示例
现在来看一个更实际的例子。
在下面的代码中,我们尝试读取 CSV 文件。当引发
FileNotFoundError异常时,代码会打印该错误以及关于data.csv的附加提示。没错,我们可以在不中断执行的情况下打印默认错误消息。
The try with else clause
我们已经学习了
try和except,接下来学习else语句。当
try语句未引发异常时,代码会进入else块。它是一种补救或回退选项,适用于您预期脚本某部分可能会产生异常的情况。通常用于简短的准备或校验环节,以避免掩盖某些错误。注意:在 try-except 结构中,
else需要放在所有except语句之后。简单示例
我们在
ZeroDivisionError示例中加入else。如您所见,当没有异常时,会执行else下的 print 函数并显示结果。带 else 的 IndexError 示例
我们通过创建一个简单函数并在多种场景下测试来进一步学习。
find_nth_value()函数以x(列表)和n(索引编号)为参数。我们使用了try、except和else来处理IndexError异常。列表
x含有四个值,我们将分别测试第 6 和第 2 个索引。IndexError,我们看到了默认错误消息 “list index out of range”。else语句下的结果。Python 中的 finally 关键字
在
try-except结构中,无论是否有异常,finally关键字总会被执行。finally块总是在try、except和else之后执行,不受是否引发或捕获异常的影响。它非常适合用于资源清理和对象关闭,尤其是关闭文件。divide函数用于处理ZeroDivisionError异常,并在没有异常时显示结果。无论结果如何,都会运行finally,以绿色打印 “Code by DataCamp”。第一次测试中,我们用 1 除以 0,会引发
ZeroDivisionError并打印消息。可以看到,错误信息之后还有一行输出。当输入有效时,会执行
else和finally并显示结果。如果第二个参数传入字符串而不是整数,会引发不同于
ZeroDivisionError的异常,并显示不同的信息。这三种场景有一个共同点:代码总会运行
finally下的打印语句。如果您是 Python 新手,并希望像专业程序员一样编码,可以尝试我们的 Python Programming 技能路径。您将学习高效编码、Python 函数、软件工程、单元测试与面向对象编程。
Python 中的嵌套异常处理
当我们希望程序按顺序处理多类异常时,需要使用嵌套异常处理。例如,可以在
else语句下再添加一个 try-except 块。如果第一段代码未引发异常,就接着检查第二段代码。修改 divide 函数
我们在前面的
divide函数基础上修改,在else语句下添加了一个嵌套的 try-except 块。这样,如果没有AttributeError,就进入else,并检查新代码是否会引发ZeroDivisionError。第一种场景中,我们提供含四个值的列表
x和分母 3。脚本会向列表添加 50,将列表中的每个值除以 3,并显示结果。函数成功执行,未引发任何异常。
如果第一个参数传入整数而不是列表,会引发
AttributeError。最后一种场景中,传入列表,但第二个参数为 0,在
else语句下引发ZeroDivisionError。文件编辑示例
我们来看更贴近实际的例子:加载文件、写入文本并关闭文件。
file_editor()函数将:open()函数是否引发FileNotFoundError。write()函数的异常。finally语句关闭文件。第一种场景中,我们提供文件路径与文本。
外层异常被引发。
要解决“找不到文件”的异常,我们需要使用 Linux 的
echo命令创建data.txt文件。之后,重新运行
file_editor()函数。此次引发了内层异常,因为
write()函数无法写入文本。要解决该问题,需要将第三行从
data = open(path)改为data = open(path, 'a'),这样即可向文件追加新文本。再次运行函数后,文本已成功写入文件。
并不推荐使用嵌套异常处理,因为它会增加异常处理的复杂度;开发者通常使用多个
try-except块来实现简单的顺序异常处理。注意:您也可以在
try或except下添加嵌套的try-except块,具体取决于您的需求。在 Python 中抛出异常
除了捕获异常之外,您还可以使用
raise关键字主动抛出异常。这对于执行业务规则或输入校验很有用,可以在问题进一步传导并导致更难调试的失败之前发出信号。要抛出异常,需要使用
raise关键字并跟随异常名称。抛出 ValueError 示例
我们可以在
if/else语句中直接使用 raise 抛出异常。如下示例中,若值超过 1,000 则抛出
ValueError。我们把值改为 2,000,使if条件为TRUE,并抛出带自定义消息的ValueError。自定义错误消息有助于快速定位问题。抛出异常示例
只要条件满足,我们也可以抛出任意内置异常。在此示例中,我们抛出了一个通用 Exception 并携带错误消息。
处理已抛出的异常示例
我们也可以自定义异常,并用
try-except来处理。在此示例中,我们把抛出 ValueError 的逻辑放在
try语句下。它如何工作?不是抛出异常并终止程序,而是显示我们提供的错误消息。
这种异常处理有助于我们为 Python 未涵盖、但特定于您应用需求的错误做好准备。
使用 raise ... from 进行异常链
除了单独抛出异常外,还可以使用
raise ... from进行异常链。这样会保留原始异常作为上下文,使当一个错误引发另一个错误时,回溯信息更清晰。其中
from e会在新异常上设置__cause__属性,使阅读回溯的人可以明确看到其触发原因。这在大型应用中尤为有用,因为异常会跨越多个代码层级。Python 3.10 - 3.13 中异常处理的新变化
自本文最初发布以来,较新的 Python 版本(如 3.10 与 3.11)对异常处理引入了一些重要改进:
1. Python 3.10 的结构化模式匹配
Python 3.10 引入了结构化模式匹配,可在
match语句中按错误类型匹配,从而更优雅地处理异常。对于需要处理多种异常类型、且希望可读性更强的场景尤其有用。例如:2. 更精细的回溯定位(Python 3.11)
Python 3.11 的回溯现在能显示更精确的错误位置,使调试异常更容易。
在 Python 3.11 之前:
在 Python 3.11 中,回溯会精确标注引发异常的子表达式:
3. 异常组与 except*(Python 3.11)
Python 3.11 引入异常组,可使用
except*在单个代码块中处理多个异常。这在管理异步代码或可能同时出现多个异常的场景中很有用。例如:4. 异常的 add_note 方法(Python 3.11)
异常对象现在具有
add_note()方法,开发者可以为异常添加自定义说明,便于调试。例如:5. PEP 654:更好地处理嵌套异常
借助异常组,嵌套异常在复杂工作流(如多进程或异步任务)中更易调试与处理。
6. 改进的错误消息(Python 3.12)
Python 3.12 延续了让错误消息更具体且可操作的长期改进。例如,
NameError现在会建议作用域内拼写相近的变量,而常见类型上的属性错误也会给出更有针对性的提示:7. 彩色回溯(Python 3.13)
Python 3.13 在交互式解释器中默认引入彩色回溯。错误类型、行号和消息以不同颜色高亮,尤其在包含多个异常链的长回溯中,有助于更快定位问题。
结论
单元测试(unit testing)与异常处理是 Python 编程的核心组成部分,使您的代码具备生产可用性并尽可能避免错误。本文学习了 Python 中的异常与错误,以及如何处理它们;同时了解了更复杂的嵌套 try-except 结构,并可按需创建自定义异常块。
这些工具与机制很重要,但多数情况下,简单的
try与except块就能完成主要工作:try负责捕获代码引发的异常,except负责处理这些异常。如果您仍感困惑、尚不知从何入手,建议学习我们的 Introduction to Functions in Python 课程,以理解作用域、lambda 函数与错误处理。您也可以加入 Python Programmer 职业路径,系统提升技能,成为专业的 Python 开发者。