精通Python的异常处理(try-except)
要在电脑上通过命令提示符或 PowerShell 运行 Python,需要先下载并安装 Python。
如果你还没有安装,请参考Python安装与开发环境配置一文来完成安装。
在编写程序时,遇到意想不到的错误是不可避免的。错误的原因多种多样,例如用户输入错误、文件不存在、网络问题等。在这种情况下,“异常处理”是一种可以防止程序突然崩溃并智能应对的机制。
本文将通过可立即复制粘贴并运行的代码示例,为初学者通俗易懂地解说Python中try-except语句在异常处理中的基本用法和进阶应用。让我们一边体验“让代码跑起来”的乐趣,一边掌握编写健壮程序的方法吧!
从基础开始!try-except语法
异常处理最基本的形式是try-except语句。顾名思义,它创建了一个“try(尝试),如果发生异常(错误),就except(捕获并处理)”的流程。
语法非常简单:
- 在
try代码块中,编写可能会引发错误的代码。 - 在
except代码块中,编写当错误发生时希望执行的代码。
百闻不如一见。让我们以著名的“除零错误”(ZeroDivisionError)为例。下面的代码试图将10除以0。通常情况下,这会导致程序因错误而停止,但通过try-except,我们可以妥善地捕获它。
<!-- Python代码 -->
try:
# 可能会发生错误的代码
result = 10 / 0
print(f"计算结果: {result}")
except ZeroDivisionError:
# 发生ZeroDivisionError时的处理
print("哦!不能除以0。")
print("程序已正常结束。")
捕获特定异常!多个except
在程序中,可能会发生各种类型的错误。例如,在用户输入中,“希望输入数字却输入了字符串”(ValueError),或在文件操作中,“找不到指定的文件”(FileNotFoundError)等。
通过在except语句后指定错误的类型(异常类),可以只捕获特定的错误,并对每种错误执行相应的处理。
此外,通过并列多个except,可以根据错误类型编写不同的处理逻辑。以下示例尝试将字符串转换为整数。
<!-- Python代码 -->
user_input = "hello" # 不是数字而是字符串
try:
num = int(user_input)
print(f"输入的数字: {num}")
except ValueError:
print("请输入数字!字符串无法转换为数字。")
except TypeError:
print("类型错误!该类型不能转换为数字。")
print("继续执行处理...")
很多时候,我们希望了解发生的错误的详细信息(错误消息)。在这种情况下,可以使用as将异常对象存储在变量中。通常,变量名e(error或exception的首字母)被广泛使用。
<!-- Python代码 -->
try:
# 尝试打开一个不存在的文件
with open("不存在的文件.txt", "r") as f:
content = f.read()
except FileNotFoundError as e:
# 显示错误的详细信息
print(f"找不到文件。")
print(f"错误详情: {e}")
进阶篇:熟练使用else和finally
try-except还有两个强大的伙伴:else和finally。将它们结合起来,可以实现更精细的处理。
- else代码块: 仅当
try代码块中没有发生异常时执行。当您想将正常流程的处理与错误处理明确分开时,这非常有用。 - finally代码块: 无论是否发生异常,总是在最后执行。它最适合编写清理代码,例如关闭文件或网络连接。
让我们来看一个使用了所有这些元素的代码。
<!-- Python代码 -->
file_path = "my_data.txt"
data_to_write = "这是测试数据。"
try:
print(f"正在向'{file_path}'写入数据...")
f = open(file_path, "w", encoding="utf-8")
# 如果想故意引发错误,请取消下一行的注释
# f.write(12345) # 尝试写入非字符串内容会引发TypeError
f.write(data_to_write)
print("已尝试写入。")
except TypeError as e:
print(f"发生错误!写入的数据必须是字符串。")
print(f"错误详情: {e}")
except Exception as e:
# 捕获其他意想不到的错误
print(f"发生了意想不到的错误。")
print(f"错误详情: {e}")
else:
# 在try代码块没有发生异常时执行
print("文件写入成功!")
finally:
# 无论是否发生异常,总会执行
if 'f' in locals() and not f.closed:
f.close()
print("文件已关闭。")
else:
print("文件未打开或已关闭。")
print("--- 处理完成 ---")
实践!可直接复制粘贴运行的完整代码示例
现在,让我们运用到目前为止所学的所有知识,来创建一个更实用的程序。此代码会询问用户的年龄,并重复提示直到输入有效的数字为止。然后,它会将输入的年龄保存到文件中。在此过程中可能发生的错误都由try-except处理。
请将下面的全部代码复制并保存为一个名为age_logger.py的文件,然后在终端中通过python age_logger.py来运行它。您可以故意输入一些字符,看看错误处理是如何工作的!
<!-- 完整的Python脚本: age_logger.py -->
import datetime
def record_age():
"""
一个询问用户年龄并将其记录到文件中的函数。
处理输入验证和文件I/O错误。
"""
while True:
try:
# 接收用户输入
age_str = input("请输入您的年龄(数字): ")
# 将字符串转换为整数
age = int(age_str)
# 检查负值或不切实际的年龄
if age < 0 or age > 130:
# 引发自定义错误 (raise)
raise ValueError("年龄必须在0到130岁之间。")
# 如果输入了有效年龄,则跳出循环
break
except ValueError as e:
# 捕获int()转换失败或由raise引发的ValueError
print(f"错误: {e}")
print("请再次输入正确的数字。\n")
# 记录到文件
try:
# 使用with语句会自动关闭文件,因此不需要finally来调用close()
with open("age_log.txt", "a", encoding="utf-8") as f:
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
f.write(f"[{timestamp}] 记录的年龄: {age}岁\n")
print(f"您的年龄“{age}岁”已记录到'age_log.txt'中。")
except IOError as e:
# 捕获诸如没有文件写入权限之类的错误
print("致命错误:无法写入文件。")
print(f"错误详情: {e}")
if __name__ == "__main__":
record_age()
print("感谢您的使用。")
注意事项:捕获过于宽泛的异常
异常处理虽然方便,但如果使用不当,反而会使程序中的问题更难被发现。特别需要注意的是捕获范围过广的异常。
不推荐:裸except
最糟糕的写法是不指定异常类的“裸except:”。它会无差别地捕获所有错误。这甚至包括用于终止程序的SystemExit,以及通过按Ctrl+C强制停止时产生的KeyboardInterrupt。这可能导致您想停止程序却无法停止的情况。
需注意:except Exception
except Exception as e:虽然比裸except:要好,但它仍然会捕获大多数常见的错误。在开发过程中,我们常常希望通过让错误发生来发现意想不到的bug。如果什么都捕获,那些本应被修复的bug就可能会被掩盖。
最佳实践是“只具体指定并捕获那些可预测且能妥善处理的异常”。
总结与后续步骤
这次,我们学习了Python中用于异常处理的try-except语句。让我们回顾一下要点:
- 使用
try-except可以防止程序因错误而崩溃。 - 使用
except ExceptionClass as e可以捕获特定错误并获取详细信息。 else用于处理成功时的情况,而finally用于执行清理工作。- 诀窍是将异常的捕获范围限制在具体且可处理的特定情况上,而不是过于宽泛。
掌握了异常处理,您就可以创建出对用户友好、对开发者易于维护的健壮应用程序。请务必在您的代码中引入try-except。
作为下一步,何不学习一个能让程序模块化并提高其可重用性的重要概念呢?