- Published on
3.9.模块和包
- Authors

- Name
- xiaobai
1.概述
模块和包是 Python 中组织代码的重要方式,它们让代码更加结构化、可维护和可重用。掌握模块和包的使用是 Python 编程的基础技能。
2.核心概念
- 模块:包含 Python 定义和语句的
.py文件 - 包:包含多个模块的目录,必须有
__init__.py文件 - 命名空间:每个模块和包都有独立的命名空间
- 导入:将模块或包引入当前程序的过程
3.模块(Module)
3.1.什么是模块?
模块是一个包含 Python 定义和语句的 .py 文件,可以包含:
- 函数定义
- 类定义
- 变量
- 可执行代码
3.2.模块的优势
- 代码复用:公共函数/类可以集中管理
- 命名空间隔离:避免命名冲突
- 可维护性:分文件组织更易管理大型项目
- 模块化设计:提高代码的模块化程度
3.3.创建和使用模块
3.3.1.创建模块
# mymodule.py
def hello():
print("Hello from mymodule!")
def add(a, b):
return a + b
class Calculator:
def multiply(self, a, b):
return a * b
3.3.2.导入模块
# main.py
import mymodule
# 使用模块中的函数和类
mymodule.hello()
result = mymodule.add(3, 4)
calc = mymodule.Calculator()
print(calc.multiply(2, 3))
3.3.3.导入特定成员
# 导入特定函数和类
from mymodule import hello, add, Calculator
# 直接使用,无需模块名前缀
hello()
result = add(3, 4)
calc = Calculator()
3.4.模块查找路径
Python 按照以下顺序查找模块:
- 当前程序所在目录
PYTHONPATH环境变量指定的目录- Python 安装时的标准库目录
import sys
# 查看模块搜索路径
print(sys.path)
3.5.模块命名规范
- 使用小写字母和下划线
- 避免与标准库同名
- 导入时无需写
.py后缀
4.导入方式详解
4.1.导入整个模块
import math
# 通过模块名访问成员
print(math.sqrt(16)) # 输出 4.0
print(math.pi) # 输出 3.141592653589793
4.1.1.优点
- 命名空间清晰:明确成员来自哪个模块
- 避免命名冲突:同名函数不会冲突
- 代码可读性:容易理解函数来源
4.1.2.适用场景
- 使用模块中多个成员时
- 需要明确函数来源时
- 避免命名冲突时
4.2.导入所有成员(import *)
from 模块名 import *
4.2.1.示例
# mymodule.py
def foo():
print("foo")
def bar():
print("bar")
from mymodule import *
foo() # 输出 foo
bar() # 输出 bar
4.2.2.控制导入内容
# mymodule.py
__all__ = ['foo'] # 只允许导入 foo
def foo():
print("foo")
def bar():
print("bar")
4.2.3.注意事项
- 不推荐使用:容易造成命名冲突
- 仅限模块:不适用于包
- 命名空间污染:直接暴露在当前命名空间
- 可读性差:难以确定函数来源
4.2.4.推荐做法
# 推荐:明确导入
from mymodule import foo, bar
# 推荐:导入整个模块
import mymodule
4.3.使用别名
import 模块名 as 新名字
from 模块名 import 成员 as 新名字
4.3.1.示例
import numpy as np
import matplotlib.pyplot as plt
from math import factorial as fact
# 使用别名
arr = np.array([1, 2, 3])
plt.plot([1, 2, 3])
result = fact(5)
4.3.2.别名优势
- 代码简洁:缩短长模块名
- 避免冲突:解决命名冲突
- 社区约定:遵循常用别名习惯
4.3.3.常用别名
numpy→nppandas→pdmatplotlib.pyplot→plttensorflow→tf
4.4.模块的 name 属性
__name__ 属性用于判断模块是被直接运行还是被导入:
- 直接运行时:
__name__为"__main__" - 被导入时:
__name__为模块名称
4.4.1.示例
# mymodule.py
def func():
print("hello")
if __name__ == "__main__":
# 只在直接运行时执行
func()
4.4.2.使用场景
| 运行方式 | __name__ 值 | 作用 |
|---|---|---|
| 直接运行 | "__main__" | 执行主流程 |
| 被导入 | 模块名 | 提供接口 |
4.4.3.最佳实践
- 所有模块入口脚本都使用
if __name__ == "__main__": - 既可作为工具库导入,也可作为脚本运行
- 提高代码的灵活性和可测试性
5.包(Package)
5.1.什么是包?
包是组织多个模块的目录,包含:
- 多个模块(.py 文件)
- 子包(嵌套目录)
__init__.py文件(必需)
5.2.包的优势
- 项目结构清晰:分层组织代码
- 避免命名冲突:独立的命名空间
- 便于团队开发:模块化协作
- 组件重用:可复用的代码单元
5.3.包的结构
mypackage/
├── __init__.py # 包初始化文件(必需)
├── module1.py # 模块1
├── module2.py # 模块2
└── subpackage/ # 子包
├── __init__.py # 子包初始化文件
└── module3.py # 子包模块
5.4.创建包结构
5.4.1.目录结构
myproject/
├── main.py
└── mypackage/
├── __init__.py
├── module1.py
├── module2.py
└── subpackage/
├── __init__.py
└── module3.py
5.4.2.初始化文件 init.py
# mypackage/__init__.py
"""mypackage 包的初始化文件"""
__version__ = "1.0.0"
__author__ = "Your Name"
# 导入包中的模块,方便用户使用
from .module1 import function1
from .module2 import Class2
# 定义包级别的变量
package_variable = "这是包变量"
print("mypackage 包被导入")
5.4.3.包中的模块
# mypackage/module1.py
def function1():
return "这是 module1 的 function1"
def helper_function():
return "辅助函数"
# mypackage/module2.py
class Class2:
def __init__(self, value):
self.value = value
def display(self):
return f"Class2 的值: {self.value}"
# mypackage/subpackage/module3.py
def function3():
return "这是子包中的 function3"
5.5.导入包和模块语法
5.5.1.导入整个包
# 导入整个包(执行 __init__.py)
import mypackage
5.5.2.导入包内模块
# 导入包内指定模块
from mypackage import module1
# 导入包中模块的成员
from mypackage.module1 import function1
5.5.3.导入子包
# 导入子包中的模块
from mypackage.subpackage import module3
# 导入子包模块的成员
from mypackage.subpackage.module3 import function3
5.6.相对导入与绝对导入
5.6.1.绝对导入
从项目根包开始写完整路径:
# 从 mypackage 包中的 module1 模块导入 function1 函数
from mypackage.module1 import function1
# 从子包中导入
from mypackage.subpackage.module3 import function3
5.6.2.相对导入
在包内部使用点号语法:
# 同级目录
from . import module1
# 上一级包
from .. import module2
# 同级模块
from .module1 import func
# 上级包的子包
from ..subpackage import module3
5.6.3.注意事项
- 相对导入只能用于包内部模块
- 不能在最顶层脚本(main.py)中使用
- 包内各文件的相对路径以包目录为基准
5.7.init.py 的作用
- 声明包:标识目录为 Python 包
- 初始化操作:包级变量、配置、统一导入
- 控制导入:通过
__all__列表控制可见成员
5.8.导入包和模块示例
# main.py
# 导入包中的特定模块
from mypackage import module1
print(module1.function1())
# 导入包中的特定函数/类
from mypackage.module2 import Class2
obj = Class2(42)
print(obj.display())
# 导入子包中的模块
from mypackage.subpackage import module3
print(module3.function3())
# 使用 __init__.py 中导入的内容
from mypackage import function1
print(function1())
# 导入包变量
import mypackage
print(mypackage.package_variable)
5.9.输出结果
mypackage 包被导入
这是 module1 的 function1
Class2 的值: 42
这是子包中的 function3
这是 module1 的 function1
这是包变量
6.高级导入技巧
6.1.相对导入
相对导入在包内部使用点号语法引用同包或父包内容:
6.1.1.基本语法
from . import xxx:从当前包导入from .. import xxx:从父包导入from .subpackage import yyy:从子包导入
6.1.2.示例
mypackage/
├── __init__.py
├── module1.py
├── module2.py
└── subpackage/
├── __init__.py
└── module3.py
# module1.py
from . import module2 # 导入同级模块
from .subpackage import module3 # 导入子包模块
def function1():
return "这是 module1 的 function1"
6.1.3.注意事项
- 只能在包内部使用
- 不能在主程序(main.py)中使用
- 有助于包的重命名与迁移
- 点数与目录层次相关
6.2.动态导入
动态导入在程序运行时根据需要导入模块,常用于插件系统和可扩展框架。
6.2.1.使用 import()
module_name = "math"
math_module = __import__(module_name)
print(math_module.sqrt(9)) # 输出: 3.0
6.2.2.使用 importlib
import importlib
# 导入标准库模块
module = importlib.import_module("json")
data = module.loads('{"a": 1}')
print(data) # 输出: {'a': 1}
# 导入包内模块
module_name = "mypackage.module1"
mod = importlib.import_module(module_name)
result = mod.function1()
print(result) # 输出: 这是 module1 的 function1
6.2.3.动态导入优势
- 按需加载:减少资源占用
- 插件机制:支持动态扩展功能
- 配置驱动:根据配置加载不同组件
6.2.4.注意事项
- 确保模块名合法且可找到
- 过度使用影响代码可读性
- 仅在确有需要时使用
7.模块搜索路径
7.1.搜索路径顺序
Python 按以下顺序查找模块:
- 当前脚本所在目录
- 标准库路径(如
/usr/lib/python3.x) - 第三方库目录(如
site-packages) PYTHONPATH环境变量指定的路径
7.2.查看和修改搜索路径
import sys
# 查看当前搜索路径
print(sys.path)
# 临时添加自定义路径
sys.path.append('/path/to/your/modules')
7.3.模块查找优先级
- 内存缓存:已加载的模块(防止重复导入)
- 内建模块:如
sys、os - sys.path 目录:按顺序逐一查找
7.4.最佳实践
- 合理组织项目结构
- 使用
__init__.py标识包 - 规范包和模块命名
- 避免随意修改
sys.path
8.创建可安装的包
8.1.包目录结构
mypackage/
├── mypackage/
│ ├── __init__.py
│ ├── module1.py
│ └── module2.py
├── tests/
│ └── test_module1.py
├── setup.py
├── README.md
└── LICENSE
8.2.创建步骤
- 组织包目录结构
- 外层目录:项目根目录
- 内层目录:实际包目录(包含
__init__.py) tests/:测试代码目录
- 创建
__init__.py- 声明目录为 Python 包
- 可包含包级初始化逻辑
- 编辑
setup.py配置- 声明包名称、版本、依赖等元信息
- (可选)增加
MANIFEST.in- 包含非 Python 文件(数据文件、README 等)
- 安装和发布
- 本地安装:
pip install .或pip install -e . - 发布到 PyPI:
python setup.py sdist bdist_wheel和twine upload dist/*
- 本地安装:
8.3.设置文件 setup.py
# setup.py
from setuptools import setup, find_packages
setup(
name="mypackage",
version="1.0.0",
description="一个示例包",
author="Your Name",
packages=find_packages(),
install_requires=[
'requests>=2.25.1',
'numpy>=1.19.5',
],
python_requires='>=3.6',
)
8.4.安装和发布
# 开发模式安装(可编辑模式)
pip install -e .
# 构建分发包
python setup.py sdist bdist_wheel
# 上传到PyPI
twine upload dist/*
9.最佳实践
9.1.命名规范
- 模块命名:使用小写字母和下划线
- 包命名:使用小写字母,避免使用下划线
- 避免冲突:不与标准库同名
9.2.导入规范
- 导入顺序:标准库 → 第三方库 → 本地模块
- 避免循环导入:模块间相互导入
- 使用
if __name__ == "__main__":区分直接运行和导入
9.3.导入顺序示例
# 标准库
import os
import sys
from datetime import datetime
# 第三方库
import requests
import numpy as np
# 本地模块
from mypackage import module1
from mypackage.subpackage import module2
9.4.项目组织
- 合理使用包和模块
- 避免过深的嵌套结构
- 保持模块功能单一
- 编写清晰的文档和注释

