到目前为止,我们已经掌握了 Python 的基本数据类型、控制流程以及函数的定义和使用。但是,当你的程序变得越来越大,代码量越来越多时,把所有代码都放在一个文件里会变得非常笨重且难以管理。Python 提供了模块(Modules) 和 包(Packages) 的机制来帮助我们更好地组织和重用代码。此外,实际应用中,程序经常需要读写文件来处理数据,本篇也将介绍文件操作。
1. 模块 (Modules)
一个 模块 就是一个包含 Python 代码(函数、类、变量等)的 .py 文件。通过模块,你可以将相关的代码组织在一起,并在其他 Python 程序中导入和使用它们。
1.1 模块的导入 (import)
要在一个 Python 文件中使用另一个模块中定义的代码,你需要使用 import 语句。
1.1.1 导入整个模块
最常见的导入方式是导入整个模块。这会创建一个模块对象,你可以通过 模块名.成员名 的方式访问其中的函数、变量等。
# 假设我们有一个名为 my_module.py 的文件,内容如下:
# --- my_module.py ---
# PI = 3.14159
#
# def greet(name):
# return f"Hello, {name}!"
#
# class MyClass:
# def __init__(self, value):
# self.value = value
# def get_value(self):
# return self.value
# --- my_module.py ---
# 在另一个文件 (比如 main.py) 中导入并使用
import my_module
print(my_module.PI) # 访问模块中的变量
print(my_module.greet("Alice")) # 调用模块中的函数
my_obj = my_module.MyClass(100) # 创建模块中的类实例
print(my_obj.get_value())
输出:
3.14159
Hello, Alice!
100
这种方式的优点是清晰明了,你知道你正在使用的函数或变量来自哪个模块,避免命名冲突。
1.1.2 从模块中导入特定成员 (from ... import ...)
如果你只需要模块中的少数几个函数或变量,可以使用 from ... import ... 语句直接导入它们,这样在使用时就不需要加上模块名前缀了。
# 假设仍有 my_module.py 文件
from my_module import PI, greet
print(PI) # 直接使用 PI
print(greet("Bob")) # 直接使用 greet 函数
# from my_module import MyClass # 如果也想用 MyClass,需要单独导入
# obj = MyClass(200)
输出:
3.14159
Hello, Bob!
这种方式的优点是代码更简洁,但缺点是可能会引入命名冲突,尤其是当你导入多个模块,而它们有同名成员时。
1.1.3 导入所有成员 (from ... import *)
你也可以使用 from ... import * 来导入模块中的所有公共成员。
# 假设仍有 my_module.py 文件
from my_module import *
print(PI) # 直接使用 PI
print(greet("Charlie")) # 直接使用 greet 函数
my_obj = MyClass(300) # 直接使用 MyClass
print(my_obj.get_value())
输出:
3.14159
Hello, Charlie!
300
警告: 这种方式强烈不推荐在生产代码中使用。它会把模块里所有的公共名称都导入到当前命名空间,极易造成命名冲突,使代码难以阅读和维护。你可能不确定某个函数是自己定义的还是从哪个模块导入的。
1.1.4 导入并重命名 (import ... as ...)
如果你想给导入的模块或成员起一个更短或更具描述性的别名,可以使用 as 关键字。
# 假设仍有 my_module.py 文件
import my_module as mm # 给模块起一个别名 mm
print(mm.PI)
print(mm.greet("David"))
from my_module import greet as say_hello # 给函数起一个别名
print(say_hello("Eve"))
输出:
3.14159
Hello, David!
Hello, Eve!
这种方式在模块名很长或为了避免导入名称冲突时非常有用。
1.2 Python 模块的搜索路径
当 Python 解释器遇到 import 语句时,它会按照一定的顺序查找模块:
- 当前目录: 查找当前执行脚本所在的目录。
PYTHONPATH环境变量: 查找PYTHONPATH环境变量指定的目录列表。- 标准库目录: 查找 Python 安装目录下的标准库目录。
site-packages目录: 查找第三方库安装目录(通常在 Python 安装目录下的site-packages)。
你可以通过查看 sys.path 列表来了解 Python 的搜索路径:
import sys
print(sys.path)
这会打印出一个包含所有搜索路径的列表。
1.3 常用标准库模块介绍
Python 拥有一个庞大且功能丰富的标准库,它们随 Python 安装一同提供,无需额外安装。这些模块覆盖了从数学运算到网络通信的各种功能。以下是一些最常用的标准库模块:
math: 提供数学函数,如sqrt()(平方根),sin()(正弦),cos()(余弦),pi(圆周率) 等。import math print(math.sqrt(16)) print(math.pi)random: 用于生成伪随机数。常用于游戏、模拟和数据混洗。import random print(random.randint(1, 10)) # 生成 1 到 10 之间的随机整数(包含 1 和 10) print(random.choice(['apple', 'banana', 'cherry'])) # 从序列中随机选择一个元素datetime: 处理日期和时间的模块。import datetime now = datetime.datetime.now() print(f"当前日期和时间: {now}") print(f"年份: {now.year}")os: 提供与操作系统交互的功能,如文件和目录操作。import os print(f"当前工作目录: {os.getcwd()}") # 获取当前工作目录 # os.mkdir("new_directory") # 创建新目录 # os.remove("my_file.txt") # 删除文件sys: 提供对 Python 解释器相关信息的访问。我们上面用过sys.path。import sys print(f"Python 版本: {sys.version}")json: 用于处理 JSON (JavaScript Object Notation) 数据,常用于数据交换。import json data = {'name': 'Alice', 'age': 30} json_string = json.dumps(data) # 将 Python 字典转换为 JSON 字符串 print(f"JSON 字符串: {json_string}") loaded_data = json.loads(json_string) # 将 JSON 字符串解析为 Python 字典 print(f"解析后的数据: {loaded_data['name']}")collections: 提供了高级的数据结构,如defaultdict,Counter,deque等。from collections import Counter words = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'] word_counts = Counter(words) # 统计元素出现次数 print(f"单词计数: {word_counts}")
2. 包 (Packages)
随着项目规模的增长,模块的数量也会越来越多。为了更好地组织这些模块,Python 引入了包的概念。一个包本质上是一个包含多个模块(以及其他子包)的目录。
一个目录要被 Python 视为一个包,它必须包含一个名为 __init__.py 的特殊文件(在 Python 3.3+ 中,即使没有这个文件,只要目录中包含 Python 模块,也可以被视为包,但为了兼容性和明确性,强烈建议保留它)。
2.1 包的结构
一个典型的包结构可能如下所示:
my_project/
├── main.py
└── my_package/
├── __init__.py
├── module_a.py
└── sub_package/
├── __init__.py
└── module_b.py
my_project/: 项目根目录main.py: 主程序文件my_package/: 一个包__init__.py: 使my_package成为一个包。这个文件可以为空,也可以包含包初始化代码。module_a.py:my_package下的一个模块。sub_package/:my_package下的一个子包。__init__.py: 使sub_package成为一个子包。module_b.py:sub_package下的一个模块。
2.2 包的导入与使用
导入包中的模块或成员与导入普通模块类似,只是需要指定完整的路径(用点 . 分隔)。
# 假设我们有上述包结构,并在 main.py 中进行导入和使用
# 导入包中的模块
import my_package.module_a
print(my_package.module_a.some_function_in_a())
# 导入子包中的模块
import my_package.sub_package.module_b
print(my_package.sub_package.module_b.some_function_in_b())
# 从包中导入特定成员
from my_package.module_a import some_function_in_a
print(some_function_in_a())
# 从子包中导入特定成员并重命名
from my_package.sub_package.module_b import some_function_in_b as func_b
print(func_b())
通过包,我们可以建立清晰的代码结构,避免命名冲突,并提高代码的可维护性。
3. 文件操作 (File I/O)
程序经常需要读写文件来保存数据或加载配置。Python 提供了内置函数 open() 来进行文件操作。
3.1 打开文件 (open())
open() 函数用于打开文件,并返回一个文件对象。它至少需要一个参数:文件路径(包含文件名)。第二个参数是可选的,表示文件打开模式。
常用文件模式:
'r'(read): 读取模式(默认)。文件必须存在。'w'(write): 写入模式。如果文件不存在则创建;如果文件存在则清空文件内容。'a'(append): 追加模式。如果文件不存在则创建;如果文件存在则在文件末尾添加内容。'x'(exclusive creation): 排他创建模式。如果文件存在则报错。'b'(binary): 二进制模式(例如用于图像、音频文件),需要与'r','w','a'等组合使用,如'rb','wb'。't'(text): 文本模式(默认)。
# 写入文件示例
# 如果文件不存在,会创建文件;如果文件存在,会覆盖原内容
file = open('my_file.txt', 'w', encoding='utf-8') # 推荐指定编码
file.write("Hello, Python!\n")
file.write("This is a new line.\n")
file.close() # 写入后务必关闭文件!
# 追加文件示例
file = open('my_file.txt', 'a', encoding='utf-8')
file.write("Appending this line.\n")
file.close()
3.2 读取文件
有几种方法可以从文件中读取内容:
read(): 读取整个文件的内容作为一个字符串。readline(): 读取文件中的一行。readlines(): 读取所有行并返回一个字符串列表,每个元素是一行。- 直接遍历文件对象: 这是最常用和高效的方法,可以逐行读取文件内容。
# 读取文件示例
file = open('my_file.txt', 'r', encoding='utf-8')
# 方法1: 读取整个文件
content = file.read()
print("--- 文件全部内容 ---")
print(content)
file.close()
# 重新打开文件进行其他读取
file = open('my_file.txt', 'r', encoding='utf-8')
# 方法2: 逐行读取
print("\n--- 逐行读取 ---")
line1 = file.readline()
print(f"第一行: {line1.strip()}") # .strip() 用于去除行末的换行符
line2 = file.readline()
print(f"第二行: {line2.strip()}")
file.close()
# 重新打开文件进行其他读取
file = open('my_file.txt', 'r', encoding='utf-8')
# 方法3: 遍历文件对象 (推荐方式,高效且内存友好)
print("\n--- 遍历文件对象逐行读取 ---")
for line in file:
print(f"遍历行: {line.strip()}")
file.close()
输出(基于之前的写入和追加操作):
--- 文件全部内容 ---
Hello, Python!
This is a new line.
Appending this line.
--- 逐行读取 ---
第一行: Hello, Python!
第二行: This is a new line.
--- 遍历文件对象逐行读取 ---
遍历行: Hello, Python!
遍历行: This is a new line.
遍历行: Appending this line.
3.3 自动关闭文件 (with 语句)
每次手动调用 file.close() 容易遗漏,并且在文件操作过程中发生异常时可能导致文件无法关闭。Python 提供了 with 语句(上下文管理器)来确保文件在使用完毕后自动关闭,即使发生错误也不例外。这是推荐的文件操作方式。
# 使用 with 语句写入
with open('another_file.txt', 'w', encoding='utf-8') as f:
f.write("This is written using with statement.\n")
f.write("It ensures the file is closed automatically.\n")
# 使用 with 语句读取
with open('another_file.txt', 'r', encoding='utf-8') as f:
content = f.read()
print("\n--- 使用 with 语句读取 ---")
print(content)
print("文件已自动关闭。") # 即使上面读取或写入时发生错误,文件也会被关闭
总结
本篇我们学习了 Python 代码组织和文件交互的关键概念:
- 模块: 将代码封装在
.py文件中,通过import语句重用。 - 导入方式:
import module_name,from module_name import member,from module_name import *,import module_name as alias。 - 标准库: 熟悉并利用 Python 强大的内置模块,如
math,random,datetime,os,sys,json,collections。 - 包: 使用目录和
__init__.py文件来组织更大型的项目和模块。 - 文件操作: 使用
open()函数进行文件的读 ('r')、写 ('w')、追加 ('a'),以及最重要的**with语句**来确保文件被安全地关闭。
掌握这些,你就能够编写出结构化、可重用且能与外部数据交互的更强大的 Python 程序了。
练习题
尝试独立完成以下练习题,并通过答案进行对照:
-
模块导入与使用:
- 导入
math模块,计算并打印123.45的平方根。 - 从
random模块导入randint函数,生成并打印一个 1 到 100 之间的随机整数。 - 导入
datetime模块并取别名为dt,打印当前的完整日期和时间。
- 导入
-
自定义模块:
- 创建一个名为
my_utils.py的文件,并在其中定义两个函数:add(a, b): 返回a和b的和。subtract(a, b): 返回a减去b的差。
- 在另一个 Python 文件(例如
main_app.py)中导入my_utils模块,并调用这两个函数,打印结果。
- 创建一个名为
-
包的使用:
- 按照以下结构创建目录和文件:
my_project/ ├── main_package_app.py └── utils/ ├── __init__.py ├── calculator.py └── text_tools.py calculator.py中定义一个multiply(a, b)函数,返回乘积。text_tools.py中定义一个reverse_string(s)函数,返回反转的字符串。- 在
main_package_app.py中,从utils.calculator导入multiply,并从utils.text_tools导入reverse_string,调用它们并打印结果。
- 按照以下结构创建目录和文件:
-
文件写入:
- 使用
with open()语句,以写入模式 ('w') 打开一个名为greetings.txt的文件。 - 向文件中写入三行问候语,每行一句。
- 使用
-
文件读取:
- 使用
with open()语句,以读取模式 ('r') 打开上面创建的greetings.txt文件。 - 使用循环逐行读取文件内容,并打印每一行(去除末尾的换行符)。
- 使用
-
文件追加:
- 使用
with open()语句,以追加模式 ('a') 打开greetings.txt文件。 - 向文件中追加一行新的问候语。
- 再次读取整个文件内容,验证新的问候语是否已添加。
- 使用
练习题答案
1. 模块导入与使用:
# 1.1 导入 math 模块并使用
import math
num = 123.45
print(f"{num} 的平方根是: {math.sqrt(num)}")
# 1.2 从 random 导入 randint 函数并使用
from random import randint
random_int = randint(1, 100)
print(f"1 到 100 之间的随机整数: {random_int}")
# 1.3 导入 datetime 模块并重命名
import datetime as dt
current_time = dt.datetime.now()
print(f"当前完整日期和时间: {current_time}")
2. 自定义模块:
首先,创建 my_utils.py 文件,内容如下:
# --- my_utils.py ---
def add(a, b):
"""返回两个数字的和。"""
return a + b
def subtract(a, b):
"""返回 a 减去 b 的差。"""
return a - b
然后,创建 main_app.py 文件(与 my_utils.py 放在同一目录下),内容如下:
# --- main_app.py ---
import my_utils # 导入整个模块
result_add = my_utils.add(10, 5)
print(f"10 + 5 = {result_add}")
result_subtract = my_utils.subtract(10, 5)
print(f"10 - 5 = {result_subtract}")
# 也可以这样导入特定函数
from my_utils import add as my_add_func
print(f"使用别名调用加法: {my_add_func(20, 8)}")
运行 main_app.py,输出:
10 + 5 = 15
10 - 5 = 5
使用别名调用加法: 28
3. 包的使用:
首先,按照结构创建目录和文件:
my_project/
├── main_package_app.py
└── utils/
├── __init__.py
├── calculator.py
└── text_tools.py
utils/__init__.py 可以为空文件,或者包含一些初始化代码(本例为空)。
utils/calculator.py 内容:
# --- utils/calculator.py ---
def multiply(a, b):
"""返回两个数字的乘积。"""
return a * b
utils/text_tools.py 内容:
# --- utils/text_tools.py ---
def reverse_string(s):
"""反转字符串。"""
return s[::-1]
main_package_app.py 内容(与 utils 目录在同一目录下):
# --- main_package_app.py ---
# 从 utils.calculator 导入 multiply 函数
from utils.calculator import multiply
# 从 utils.text_tools 导入 reverse_string 函数
from utils.text_tools import reverse_string
# 调用函数并打印结果
product = multiply(7, 8)
print(f"7 * 8 = {product}")
reversed_text = reverse_string("Hello Python")
print(f"'Hello Python' 反转后是: '{reversed_text}'")
运行 main_package_app.py,输出:
7 * 8 = 56
'Hello Python' 反转后是: 'nohtyP olleH'
4. 文件写入:
# 4. 文件写入:
file_name = "greetings.txt"
with open(file_name, 'w', encoding='utf-8') as f:
f.write("Hello from the file!\n")
f.write("Welcome to file operations in Python.\n")
f.write("Hope you're learning a lot!\n")
print(f"文件 '{file_name}' 已写入。")
5. 文件读取:
# 5. 文件读取:
file_name = "greetings.txt"
print(f"\n--- 读取文件 '{file_name}' 的内容 ---")
try:
with open(file_name, 'r', encoding='utf-8') as f:
for line in f:
print(line.strip()) # 使用 .strip() 去除每行末尾的换行符
except FileNotFoundError:
print(f"错误: 文件 '{file_name}' 不存在。")
输出(取决于你运行练习 4 的结果):
--- 读取文件 'greetings.txt' 的内容 ---
Hello from the file!
Welcome to file operations in Python.
Hope you're learning a lot!
6. 文件追加:
# 6. 文件追加:
file_name = "greetings.txt"
new_greeting = "This line was appended later.\n"
with open(file_name, 'a', encoding='utf-8') as f:
f.write(new_greeting)
print(f"\n新行 '{new_greeting.strip()}' 已追加到文件 '{file_name}'。")
print(f"\n--- 再次读取文件 '{file_name}' 的全部内容 ---")
with open(file_name, 'r', encoding='utf-8') as f:
full_content = f.read()
print(full_content)
输出(基于之前的写入和读取,现在多了追加的内容):
新行 'This line was appended later.' 已追加到文件 'greetings.txt'。
--- 再次读取文件 'greetings.txt' 的全部内容 ---
Hello from the file!
Welcome to file operations in Python.
Hope you're learning a lot!
This line was appended later.
Python模块、包与文件操作详解

被折叠的 条评论
为什么被折叠?



