一、引言:让程序优雅地面对错误
在之前的博客中,我们学习了函数进阶和文件操作。程序运行时,我们总是假设一切"完美"------文件存在、类型正确、网络畅通。但现实世界充满了不确定性:用户可能删除了文件,网络可能突然断开,数据格式可能异常......
当这些"意外"发生时,Python会抛出异常(Exception) ,程序会崩溃并显示一堆红色的错误信息。这显然不是我们想要的结果。我们需要的是:当异常发生时,程序能够"优雅地"处理它,给出友好的提示,而不是直接崩溃。
与此同时,当我们的项目越来越庞大,代码量越来越多时,如何高效地组织代码、复用功能?答案就是------模块(Module) 和包(Package)。
本篇博客将系统讲解Python的异常处理 、模块导入 和包管理三大核心技能,让你写出更健壮、更易维护的程序。
二、Python异常处理
2.1 什么是异常?
异常是指程序运行时检测到的错误。当Python解释器遇到无法继续执行的错误时,就会抛出异常并停止运行。
python
# 打开一个不存在的文件
f = open("xxx.txt", "r", encoding="utf-8")
# FileNotFoundError: [Errno 2] No such file or directory: 'xxx.txt'
上面的例子中,FileNotFoundError就是一个异常。程序直接崩溃,后续代码无法执行。
2.2 为什么要捕获异常?
世界上没有完美的程序,任何程序运行时都可能出现异常。我们要做的是:对可能出现的错误提前做好准备,当异常发生时,有后续的应对手段。
捕获异常可以让程序在出错时"优雅地降级",而不是"粗暴地崩溃"。
2.3 异常捕获的完整语法
Python提供了try-except结构来捕获和处理异常。
(1)捕获常规异常
python
try:
# 可能会发生错误的代码
f = open("xxx.txt", "r", encoding="utf-8")
except:
# 如果出现异常执行的代码
print("文件不存在,已自动创建")
f = open("xxx.txt", "w", encoding="utf-8")
(2)捕获指定异常
可以捕获指定类型的异常,并获取异常详情。
python
try:
print(name) # NameError
except NameError as e:
print("变量未定义")
print(e) # name 'name' is not defined
(3)捕获多个异常
使用元组可以同时捕获多种异常。
python
try:
print(name) # NameError
print(1 / 0) # ZeroDivisionError
except (NameError, ZeroDivisionError) as e:
print("出现变量未定义或除以零异常")
print(e)
(4)捕获所有异常
Exception是所有异常的基类,可以捕获任何异常。
python
try:
print(name)
print(1 / 0)
except Exception as e:
print("程序出现异常")
print(e)
(5)异常else
else表示没有异常时要执行的代码。
python
try:
print("正常执行")
except Exception as e:
print("出现异常")
else:
print("没有异常,一切正常")
(6)异常finally
finally表示无论是否异常,都要执行的代码,常用于资源清理。
python
try:
f = open("test.txt", "r")
except Exception as e:
print("文件打开失败")
finally:
print("无论是否异常,我都会执行")
# 可在此关闭文件等清理操作
2.4 异常的传递性
异常具有传递性:如果在函数A中发生异常且未被捕获,异常会向调用方传递------传递给函数B,再传递给main函数。如果所有函数都不处理,程序最终崩溃。
python
def fun01():
print("fun01开始")
num = 1 / 0 # 这里发生异常
print("fun01结束")
def fun02():
print("fun02开始")
fun01() # 异常向上传递
print("fun02结束")
def main():
try:
fun02()
except Exception as e:
print("捕获到异常:", e)
main()
# 输出:捕获到异常: division by zero
利用异常传递性的好处 :我们可以在程序入口(如main函数)统一捕获异常,确保无论何处发生异常,程序都不会崩溃。
三、Python模块
3.1 什么是模块
模块(Module) 是一个以.py结尾的Python文件。模块中可以定义函数、类、变量,也可以包含可执行代码。
简单来说:模块就是一个"工具包",里面封装了各种功能,我们可以导入并使用它们。
Python内置了大量实用模块,如:
-
time:时间相关功能 -
random:随机数生成 -
os:操作系统交互 -
math:数学运算
3.2 模块的导入方式
(1)import 模块名
导入整个模块,使用时通过模块名.功能名()调用。
python
import time
print("开始")
time.sleep(2) # 调用time模块的sleep功能
print("结束")
(2)from 模块名 import 功能名
导入模块中的特定功能,使用时直接写功能名。
python
from time import sleep
print("开始")
sleep(2) # 不需要写 time.
print("结束")
(3)from 模块名 import *
导入模块的所有功能(不推荐,容易造成命名冲突)。
python
from time import *
print(localtime()) # 直接使用time的所有功能
sleep(2)
(4)import 模块名 as 别名
给模块起别名,简化长名称。
python
import time as t
t.sleep(2) # 使用别名调用
print(t.localtime())
(5)from 模块名 import 功能名 as 别名
给导入的功能起别名。
python
from time import sleep as s
s(2) # 使用别名调用
3.3 导入顺序建议
-
内置模块(如
time、os) -
第三方模块(如
numpy、pandas) -
自定义模块
3.4 自定义模块
我们可以创建自己的.py文件作为模块,供其他程序导入使用。
创建my_module1.py:
python
def test(a, b):
print(f"my_module1 计算:{a} + {b} = {a + b}")
在另一个文件中导入使用:
python
import my_module1
my_module1.test(3, 5) # my_module1 计算:3 + 5 = 8
3.5 模块的__name__变量
每个模块都有一个__name__属性:
-
当模块被直接运行 时,
__name__的值为"__main__" -
当模块被导入 时,
__name__的值为模块名
这个特性常用于编写"既可作为模块导入,又可独立运行"的代码:
python
# my_module1.py
def test(a, b):
print(f"计算结果:{a + b}")
if __name__ == "__main__":
# 只有直接运行此文件时才执行
test(1, 2)
3.6 __all__变量
在模块中定义__all__列表,可以控制from module import *时导出的内容。
python
# my_module2.py
__all__ = ['test1', 'test2'] # 只允许导入test1和test2
def test1(a, b):
print(a + b)
def test2(a, b):
print(a * b)
def test3(a, b):
print(a - b)
导入时,test3不会被导入。
四、Python包
4.1 什么是包?
当模块越来越多时,我们需要用**包(Package)**来组织管理它们。
包是一个包含__init__.py文件的文件夹,它可以包含多个模块文件。
python
my_package/ # 包
__init__.py # 标识这是一个包
my_module1.py # 模块1
my_module2.py # 模块2
my_module3.py # 模块3
4.2 包的作用
-
物理角度 :包是一个文件夹,包含
__init__.py -
逻辑角度:包的本质依然是模块,用于组织功能相关的模块
4.3 导入包的方式
方式一:import 包名.模块名
python
import my_package.my_module1
my_package.my_module1.info_print()
方式二:from 包名 import *
需要在__init__.py中配置__all__变量,控制允许导入的模块列表。
python
# my_package/__init__.py
__all__ = ['my_module1', 'my_module2']
python
from my_package import *
my_module1.info_print()
my_module2.info_print()
4.4 安装第三方包
Python拥有庞大的第三方生态,可通过pip工具安装。
python
pip install 包名
常用国内镜像源:
-
清华大学:
https://pypi.tuna.tsinghua.edu.cn/simple/ -
阿里云:
http://mirrors.aliyun.com/pypi/simple/
指定镜像源安装:
python
pip install numpy -i https://pypi.tuna.tsinghua.edu.cn/simple/
五、异常、模块与包的对比总结
| 概念 | 定义 | 作用 | 关键语法 |
|---|---|---|---|
| 异常 | 程序运行时的错误 | 处理错误,防止程序崩溃 | try-except-else-finally |
| 异常传递 | 异常向调用方逐级传递 | 在统一入口捕获异常 | 在main函数设置try-except |
| 模块 | .py文件 |
封装功能,实现代码复用 | import、from ... import |
| 包 | 包含__init__.py的文件夹 |
组织管理多个模块 | import 包.模块 |
六、综合实战:健壮的文件读取程序
综合运用异常处理、文件操作和模块知识,编写一个"安全读取文件"的程序:
python
import os
def safe_read_file(filename):
"""
安全地读取文件内容,若文件不存在则创建并返回空字符串
"""
try:
with open(filename, "r", encoding="utf-8") as f:
content = f.read()
return content
except FileNotFoundError:
print(f"文件 {filename} 不存在,已自动创建")
# 创建空文件
with open(filename, "w", encoding="utf-8") as f:
f.write("")
return ""
except PermissionError:
print(f"没有权限读取文件 {filename}")
return None
except Exception as e:
print(f"读取文件时发生未知错误:{e}")
return None
def main():
filename = "data.txt"
content = safe_read_file(filename)
if content is not None:
print(f"文件内容长度:{len(content)}")
else:
print("读取失败")
if __name__ == "__main__":
main()
七、总结
本篇博客系统学习了三个重要的Python进阶概念:
| 主题 | 核心知识点 |
|---|---|
| 异常处理 | try-except捕获异常、else无异常执行、finally总是执行、异常传递性 |
| 模块导入 | 五种导入方式、自定义模块、__name__变量、__all__控制 |
| 包管理 | 包的结构(含__init__.py)、包导入方式、第三方包安装 |
建议:
-
永远不要忽略异常------即使不处理,也要记录下来
-
尽量捕获指定异常 ,而不是直接用
Exception------更精确 -
利用异常传递性,在程序入口统一处理
-
模块名要简洁有意义,避免和内置模块冲突
-
使用**
if __name__ == "__main__"**让模块兼具"可执行"和"可导入"两种能力 -
合理使用包组织代码,保持项目结构清晰