亲爱的亦菲彦祖们,欢迎回到我们的编程学习系列!在前面的学习中,我们已经掌握了类与对象的基本概念以及面向对象编程的核心特性。今天,我们将深入探讨模块和包,这是Python编程中组织和复用代码的重要工具。通过理解模块和包的概念及其使用方法,您将能够编写更加清晰、可维护和高效的代码。让我们一起来开启这段充满实用知识的学习之旅吧!
一、模块和包
1. 目标
通过本节学习,您将能够:
- 了解模块的基本概念及其重要性
- 掌握导入模块的多种方式
- 学会如何创建和使用自定义模块
- 理解包的概念及其使用方法
2. 模块
2.1 模块简介
**模块(Module)**是一个包含Python定义和语句的文件,文件名以.py
结尾。模块可以定义函数、类和变量,也可以包含可执行的代码。通过模块化,您可以将相关的功能组织在一起,实现代码的复用和管理。
示例:
# math_module.py
def add(a, b):
return a + b
def subtract(a, b):
return a - b
2.2 导入模块
Python提供了多种方式来导入模块,以满足不同的需求。
2.2.1 导入模块的方式
主要的导入模块方式包括:
import 模块名
from 模块名 import 功能名
from 模块名 import *
import 模块名 as 别名
from 模块名 import 功能名 as 别名
2.2.2 导入方式详解
2.2.2.1 import
语句
语法:
# 导入单个模块
import 模块名
# 导入多个模块
import 模块名1, 模块名2, ...
使用:
通过模块名调用其功能。
示例:
import math
print(math.sqrt(16)) # 输出:4.0
解释:
import math
导入了Python的内置math
模块。- 使用
math.sqrt()
调用模块中的sqrt
函数。
2.2.2.2 from...import...
语句
语法:
from 模块名 import 功能1, 功能2, ...
使用:
直接使用导入的功能,无需模块名作为前缀。
示例:
from math import sqrt, ceil
print(sqrt(25)) # 输出:5.0
print(ceil(4.2)) # 输出:5
解释:
from math import sqrt, ceil
从math
模块中导入sqrt
和ceil
函数。- 直接调用
sqrt()
和ceil()
,无需前缀math.
。
2.2.2.3 from...import *
语句
语法:
from 模块名 import *
使用:
导入模块中的所有公共功能。
示例:
from math import *
print(sin(0)) # 输出:0.0
print(pi) # 输出:3.141592653589793
注意:
- 使用
*
导入所有内容可能导致命名冲突,建议谨慎使用。 - 明确导入需要的功能,可以提高代码的可读性和安全性。
2.2.2.4 使用as
定义别名
语法:
# 为模块定义别名
import 模块名 as 别名
# 为功能定义别名
from 模块名 import 功能 as 别名
使用:
使用别名来简化代码书写或避免命名冲突。
示例:
# 模块别名
import math as mt
print(mt.sqrt(9)) # 输出:3.0
# 功能别名
from math import factorial as fact
print(fact(5)) # 输出:120
解释:
import math as mt
将math
模块重命名为mt
。from math import factorial as fact
将factorial
函数重命名为fact
。
2.3 制作模块
在Python中,每个Python文件都可以作为一个模块。自定义模块名必须符合标识符命名规则(即只能包含字母、数字和下划线,且不能以数字开头)。
2.3.1 定义模块
步骤:
- 创建一个新的Python文件,例如
my_module1.py
。 - 在文件中定义函数、类或变量。
示例:
# my_module1.py
def testA(a, b):
print(a + b)
2.3.2 测试模块
为了确保模块正常工作,可以在模块文件中添加测试代码。为了防止模块被导入时自动执行测试代码,可以使用if __name__ == '__main__':
判断。
示例:
# my_module1.py
def testA(a, b):
print(a + b)
if __name__ == '__main__':
testA(1, 1) # 仅在模块被直接运行时执行
解释:
- 当
my_module1.py
被直接运行时,会执行testA(1, 1)
,输出2
。 - 当
my_module1
被导入到其他模块时,不会执行testA(1, 1)
,避免不必要的输出。
2.3.3 调用模块
通过导入自定义模块,可以在其他Python文件中调用其功能。
示例:
# main.py
import my_module1
my_module1.testA(5, 7) # 输出:12
解释:
import my_module1
导入了自定义模块。- 使用
my_module1.testA(5, 7)
调用模块中的testA
函数。
2.4 模块定位顺序
当导入一个模块时,Python解释器会按照以下顺序搜索模块的位置:
- 当前目录:首先在当前执行脚本的目录中查找模块。
- 环境变量
PYTHONPATH
:如果在当前目录未找到,Python会搜索环境变量PYTHONPATH
中指定的目录。 - 默认路径:如果仍未找到,Python会搜索安装过程确定的默认目录(如
/usr/local/lib/python/
)。
查看模块搜索路径:
模块搜索路径存储在sys.path
变量中。可以通过以下代码查看:
import sys
print(sys.path)
注意:
- 确保自定义模块名不与Python内置模块或已安装的第三方模块名冲突,以避免导入错误的模块。
- 如果自定义模块位于非标准路径,可以通过修改
sys.path
或设置PYTHONPATH
环境变量来包含该路径。
3. 包
**包(Package)**是一个包含多个相关模块的文件夹。包通过在文件夹内添加一个特殊的__init__.py
文件来标识,__init__.py
文件可以是空的,也可以包含包的初始化代码。
3.1 制作包
步骤:
- 创建一个新的文件夹,例如
mypackage
。 - 在
mypackage
文件夹内创建一个空的__init__.py
文件。 - 在包内添加相关的模块文件,例如
my_module1.py
和my_module2.py
。
示例:
mypackage/
├── __init__.py
├── my_module1.py
└── my_module2.py
模块内容:
# my_module1.py
print(1)
def info_print1():
print('my_module1')
# my_module2.py
print(2)
def info_print2():
print('my_module2')
解释:
mypackage
文件夹通过__init__.py
文件被识别为一个包。my_module1.py
和my_module2.py
是包内的两个模块,包含各自的功能。
3.2 导入包
包中的模块可以通过多种方式导入,以便在其他Python脚本中使用。
导入包的方式:
import 包名.模块名
from 包名.模块名 import 函数名
3.2.1 体验导入包
示例:
# main.py
"""方法1"""
import mypackage.my_module1
mypackage.my_module1.info_print1()
"""方法2"""
from mypackage.my_module2 import info_print2
info_print2()
运行结果:
1
my_module1
2
my_module2
解释:
import mypackage.my_module1
导入了包mypackage
中的模块my_module1
,并调用其info_print1
函数。from mypackage.my_module2 import info_print2
从包mypackage
中的模块my_module2
导入了info_print2
函数,并直接调用。
3.3 拓展-封装学生信息管理系统成包
为了进一步实践模块和包的使用,我们可以将之前设计的学生信息管理系统封装成一个包。这样不仅可以更好地组织代码,还能方便地在不同项目中复用该系统。
步骤:
- 创建一个新的包文件夹,例如
student_management
。 - 在
student_management
文件夹内创建一个空的__init__.py
文件。 - 将之前设计的
Student
类和StudentManager
类分别放入不同的模块文件中,例如student.py
和manager.py
。
项目结构:
student_management/
├── __init__.py
├── student.py
└── manager.py
模块内容:
# student.py
class Student:
"""学生信息类"""
def __init__(self, name, chinese, math, english):
"""
初始化方法
:param name: 姓名
:param chinese: 语文成绩
:param math: 数学成绩
:param english: 英语成绩
"""
self.name = name
self.chinese = chinese
self.math = math
self.english = english
self.total = chinese + math + english # 总分
def __str__(self):
return f"{self.name}\t{self.chinese}\t{self.math}\t{self.english}\t{self.total}"
# manager.py
import json
from .student import Student
class StudentManager:
"""学生信息管理系统类"""
def __init__(self):
# 存储学员数据 -- 列表
self.student_list = []
# 程序入口函数
def run(self):
# 加载文件里面的学员数据
self.load_student()
while True:
# 显示功能菜单
self.show_menu()
# 用户输入目标功能序号
action = input('请选择您想要进行的操作(0-退出):')
# 根据用户输入的序号执行不同的功能
if action == '1':
# 新建学生信息
self.add_student()
elif action == '2':
# 显示全部信息
self.show_student()
elif action == '3':
# 查询学生信息
self.search_student()
elif action == '4':
# 删除学生信息
self.del_student()
elif action == '5':
# 修改学生信息
self.modify_student()
elif action == '0':
# 退出系统
self.save_student()
print('欢迎再次使用【学生信息管理系统】!再见!')
break
else:
print('输入错误,请重新选择!')
# 显示功能菜单
@staticmethod
def show_menu():
str_info = """**************************************************
欢迎使用【学生信息管理系统】V1.0
请选择你想要进行的操作
1. 新建学生信息
2. 显示全部信息
3. 查询学生信息
4. 删除学生信息
5. 修改学生信息
0. 退出系统
**************************************************"""
print(str_info)
# 添加学生信息
def add_student(self):
print('---------- 新建学生信息 ----------')
name = input('请输入姓名:').strip()
if not name:
print('姓名不能为空!')
return
try:
chinese = int(input('请输入语文成绩:'))
math = int(input('请输入数学成绩:'))
english = int(input('请输入英语成绩:'))
except ValueError:
print('成绩必须是整数!')
return
# 检查是否已有该学生
if any(stu.name == name for stu in self.student_list):
print(f'学生{name}已存在,无法重复添加!')
return
# 创建学生对象并添加到列表
student = Student(name, chinese, math, english)
self.student_list.append(student)
print(f'成功添加学生:{student.name}')
# 显示全部学生信息
def show_student(self):
print('---------- 显示全部学生信息 ----------')
if not self.student_list:
print('当前没有学生信息。')
return
print('姓名\t语文\t数学\t英语\t总分')
print('-' * 30)
for stu in self.student_list:
print(stu)
# 查询学生信息
def search_student(self):
print('---------- 查询学生信息 ----------')
search_name = input('请输入要查询的学生姓名:').strip()
if not search_name:
print('姓名不能为空!')
return
for stu in self.student_list:
if stu.name == search_name:
print('姓名\t语文\t数学\t英语\t总分')
print('-' * 30)
print(stu)
return
print(f'未找到名为"{search_name}"的学生。')
# 删除学生信息
def del_student(self):
print('---------- 删除学生信息 ----------')
del_name = input('请输入要删除的学生姓名:').strip()
if not del_name:
print('姓名不能为空!')
return
for stu in self.student_list:
if stu.name == del_name:
confirm = input(f'确定要删除学生"{del_name}"吗?(y/n):').strip().lower()
if confirm == 'y':
self.student_list.remove(stu)
print(f'成功删除学生:{del_name}')
else:
print('删除操作已取消。')
return
print(f'未找到名为"{del_name}"的学生。')
# 修改学生信息
def modify_student(self):
print('---------- 修改学生信息 ----------')
modify_name = input('请输入要修改的学生姓名:').strip()
if not modify_name:
print('姓名不能为空!')
return
for stu in self.student_list:
if stu.name == modify_name:
print('请输入新的信息(留空表示不修改):')
new_name = input(f'姓名(当前:{stu.name}):').strip()
new_chinese = input(f'语文成绩(当前:{stu.chinese}):').strip()
new_math = input(f'数学成绩(当前:{stu.math}):').strip()
new_english = input(f'英语成绩(当前:{stu.english}):').strip()
if new_name:
# 检查新姓名是否已存在
if any(s.name == new_name for s in self.student_list):
print(f'姓名"{new_name}"已存在,无法修改!')
return
stu.name = new_name
if new_chinese:
try:
stu.chinese = int(new_chinese)
except ValueError:
print('语文成绩必须是整数,修改失败!')
return
if new_math:
try:
stu.math = int(new_math)
except ValueError:
print('数学成绩必须是整数,修改失败!')
return
if new_english:
try:
stu.english = int(new_english)
except ValueError:
print('英语成绩必须是整数,修改失败!')
return
# 重新计算总分
stu.total = stu.chinese + stu.math + stu.english
print(f'学生"{stu.name}"的信息已更新。')
return
print(f'未找到名为"{modify_name}"的学生。')
# 保存学生信息
def save_student(self):
print('---------- 保存学生信息 ----------')
try:
with open('students.json', 'w', encoding='utf-8') as f:
# 将学生对象转换为字典列表
final_stu_list = [{
"name": stu.name,
"chinese": stu.chinese,
"math": stu.math,
"english": stu.english,
"total": stu.total
} for stu in self.student_list]
# 写入JSON文件
json.dump(final_stu_list, f, ensure_ascii=False, indent=4)
print('学生信息已保存到"students.json"。')
except Exception as e:
print(f'保存失败:{e}')
解释:
- 模块封装:将
Student
类和StudentManager
类分别放入student.py
和manager.py
模块中,组织结构更加清晰。 - 相对导入:在
manager.py
中,使用from .student import Student
进行相对导入,确保模块之间的依赖关系正确。 - 数据持久化:通过JSON格式保存和加载学生信息,实现数据的持久化。
3. 包
包是将有联系的模块组织在一起的方式,通过包可以更好地管理和复用代码。
3.1 制作包
步骤:
- 创建包文件夹:例如,创建一个名为
mypackage
的文件夹。 - 添加
__init__.py
文件:在mypackage
文件夹内创建一个空的__init__.py
文件,这个文件用于标识该文件夹为一个Python包。 - 添加模块:在包内添加相关的模块文件,例如
my_module1.py
和my_module2.py
。
示例:
mypackage/
├── __init__.py
├── my_module1.py
└── my_module2.py
模块内容:
# my_module1.py
print(1)
def info_print1():
print('my_module1')
# my_module2.py
print(2)
def info_print2():
print('my_module2')
解释:
mypackage
文件夹通过__init__.py
文件被识别为一个包。my_module1.py
和my_module2.py
是包内的两个模块,包含各自的功能。- 当导入包中的模块时,会自动执行模块内的顶级代码(如
print(1)
和print(2)
)。
3.2 导入包
包中的模块可以通过多种方式导入,以便在其他Python脚本中使用。
导入包的方式:
import 包名.模块名
from 包名.模块名 import 函数名
3.2.1 体验导入包
示例:
# main.py
"""方法1"""
import mypackage.my_module1
mypackage.my_module1.info_print1()
"""方法2"""
from mypackage.my_module2 import info_print2
info_print2()
运行结果:
1
my_module1
2
my_module2
解释:
import mypackage.my_module1
导入了包mypackage
中的模块my_module1
,并执行模块内的顶级代码print(1)
,然后调用info_print1()
函数,输出my_module1
。from mypackage.my_module2 import info_print2
从包mypackage
中的模块my_module2
导入了info_print2
函数,执行模块内的顶级代码print(2)
,然后调用info_print2()
函数,输出my_module2
。
注意:
- 导入包中的模块时,模块内的顶级代码会被执行一次。
- 使用不同的导入方式,可以根据需要选择最合适的调用方式。
总结
通过本节学习,您已经掌握了Python中模块和包的基本概念及其使用方法:
-
模块:
- 模块是一个包含Python定义和语句的文件。
- 通过
import
语句可以导入模块,并使用其中的功能。 - 自定义模块需要遵循命名规则,并通过
if __name__ == '__main__':
进行测试,避免导入时自动执行测试代码。 - Python在导入模块时遵循特定的搜索路径,确保模块能够被正确找到和导入。
-
包:
- 包是一个包含多个相关模块的文件夹,通过添加
__init__.py
文件标识。 - 包有助于组织和管理大型项目中的模块,提升代码的可维护性和复用性。
- 导入包中的模块时,可以使用多种导入方式,根据需求选择最合适的方式。
- 包是一个包含多个相关模块的文件夹,通过添加
-
实战应用:
- 将学生信息管理系统封装成包,通过模块化的设计,使系统结构更加清晰,便于扩展和维护。
掌握了模块和包的使用,您将在编程中更加游刃有余,能够高效地组织和管理代码,提升项目的开发效率和代码质量。下一篇博客中,我们将继续深入探讨异常处理,帮助您编写更加健壮和可靠的Python程序。
祝学习愉快,编程顺利!