第10章:模块与包
作为 Java 开发者,你已经熟悉了 Java 的包系统和模块系统。Python 也有自己的模块和包系统,本章将详细对比两种语言的模块与包。
10.1 模块系统对比
Java 模块系统
- 包:使用
package声明,通过目录结构组织 - 导入:使用
import语句导入包或类 - 模块:Java 9+ 引入了模块系统(JPMS),使用
module-info.java定义 - 可见性:通过访问修饰符控制(public, protected, private)
Python 模块系统
- 模块:一个 .py 文件就是一个模块
- 包:包含
__init__.py文件的目录就是一个包 - 导入:使用
import语句导入模块或包 - 可见性:通过命名约定控制(下划线前缀)
对比:
| 特性 | Java | Python |
|---|---|---|
| 模块定义 | 类文件 | .py 文件 |
| 包定义 | 目录 + package 声明 | 目录 + __init__.py 文件 |
| 导入语句 | import package.Class | import module 或 from module import function |
| 可见性 | 访问修饰符 | 命名约定(_prefix) |
| 模块系统 | JPMS(Java 9+) | 简单的模块导入系统 |
10.2 模块导入
Java 导入
// 导入单个类
import java.util.ArrayList;
// 导入整个包
import java.util.*;
// 静态导入
import static java.lang.Math.PI;
import static java.lang.Math.sqrt;
// 使用导入的类
ArrayList<String> list = new ArrayList<>();
double radius = 5;
double area = PI * radius * radius;
double length = sqrt(25);
Python 导入
# 导入整个模块
import math
# 导入模块中的特定函数或变量
from math import pi, sqrt
# 导入模块中的所有内容(不推荐)
from math import *
# 使用别名
import math as m
from math import pi as PI
# 使用导入的模块
radius = 5
area = math.pi * radius * radius
length = sqrt(25)
print(m.pi) # 使用别名
print(PI) # 使用重命名的变量
对比:
| 操作 | Java | Python |
|---|---|---|
| 导入模块 | import package | import module |
| 导入特定成员 | import package.Class | from module import member |
| 导入所有成员 | import package.* | from module import * |
| 使用别名 | 不支持 | import module as alias |
| 静态导入 | import static | 直接导入函数和变量 |
10.3 包的结构
Java 包结构
com/
└── example/
├── model/
│ ├── User.java
│ └── Product.java
├── service/
│ ├── UserService.java
│ └── ProductService.java
└── Main.java
Python 包结构
myapp/
├── __init__.py
├── model/
│ ├── __init__.py
│ ├── user.py
│ └── product.py
├── service/
│ ├── __init__.py
│ ├── user_service.py
│ └── product_service.py
└── main.py
对比:
| 特性 | Java | Python |
|---|---|---|
| 包标识 | 目录结构 + package 声明 | 目录结构 + __init__.py 文件 |
| 包导入 | import com.example.model.User | from myapp.model import user |
| 包初始化 | 无 | __init__.py 文件中的代码在包导入时执行 |
| 包级别变量 | 静态变量 | __init__.py 中的变量 |
10.4 创建和使用模块
Java 模块创建
// com/example/model/User.java
package com.example.model;
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
// com/example/Main.java
package com.example;
import com.example.model.User;
public class Main {
public static void main(String[] args) {
User user = new User("Alice", 25);
System.out.println("Name: " + user.getName());
System.out.println("Age: " + user.getAge());
}
}
Python 模块创建
# myapp/model/user.py
class User:
def __init__(self, name, age):
self.name = name
self.age = age
def get_name(self):
return self.name
def get_age(self):
return self.age
# myapp/main.py
from myapp.model.user import User
def main():
user = User("Alice", 25)
print(f"Name: {user.get_name()}")
print(f"Age: {user.get_age()}")
if __name__ == "__main__":
main()
对比:
| 操作 | Java | Python |
|---|---|---|
| 模块文件 | .java 文件 | .py 文件 |
| 类定义 | 每个文件通常一个类 | 一个文件可以多个类和函数 |
| 主入口 | main 方法 | if __name__ == "__main__" |
| 导入方式 | 包路径 + 类名 | 模块路径 + 类/函数名 |
10.5 包的初始化
Java 包初始化
Java 没有专门的包初始化机制,通常在静态代码块中初始化静态变量。
Python 包初始化
Python 使用 __init__.py 文件进行包初始化。
Python __init__.py 示例:
# myapp/__init__.py
"""MyApp package"""
__version__ = "1.0.0"
__author__ = "Alice"
# 从子模块导入常用类和函数
from .model.user import User
from .service.user_service import UserService
# 定义包级别的函数
def get_version():
return __version__
# 包导入时执行的代码
print(f"MyApp v{__version__} initialized")
使用方式:
import myapp
print(myapp.__version__)
print(myapp.__author__)
print(myapp.get_version())
# 直接使用从 __init__.py 导入的类
user = myapp.User("Bob", 30)
print(user.get_name())
10.6 模块搜索路径
Java 类路径
- Java 使用 CLASSPATH 环境变量和 -cp 参数指定类路径
- 类加载器从类路径中查找类文件
Python 模块搜索路径
- Python 使用 sys.path 列表指定模块搜索路径
- 搜索顺序:当前目录 → PYTHONPATH 环境变量 → 标准库目录 → 第三方库目录
Python 模块搜索路径示例:
import sys
# 查看模块搜索路径
print(sys.path)
# 添加自定义路径
sys.path.append("/path/to/my/modules")
# 导入自定义模块
import mymodule
10.7 相对导入
Java 相对导入
// 在 com.example.service 包中
package com.example.service;
// 相对导入(Java 7+)
import static com.example.model.User;
import static com.example.util.Validator;
Python 相对导入
# 在 myapp/service/user_service.py 中
# 相对导入
from ..model.user import User
from ..util.validator import validate
# 相对导入的不同级别
# . 表示当前包
# .. 表示父包
# ... 表示祖父包
class UserService:
def create_user(self, name, age):
validate(name, age)
return User(name, age)
对比:
| 特性 | Java | Python |
|---|---|---|
| 相对导入语法 | import static package.Class | from ..module import member |
| 导入级别 | 基于包层次 | 使用点号表示级别 |
| 适用范围 | 同一包内 | 同一包内的模块 |
10.8 第三方库管理
Java 依赖管理
- Maven:使用 pom.xml 管理依赖
- Gradle:使用 build.gradle 管理依赖
- 依赖仓库:Maven Central, JCenter 等
Python 依赖管理
- pip:Python 包安装工具
- requirements.txt:记录依赖版本
- Poetry:更现代的依赖管理工具
- 依赖仓库:PyPI (Python Package Index)
对比:
| 操作 | Java (Maven) | Python (pip) |
|---|---|---|
| 安装依赖 | mvn install | pip install package |
| 导出依赖 | mvn dependency:copy-dependencies | pip freeze > requirements.txt |
| 安装所有依赖 | mvn install | pip install -r requirements.txt |
| 依赖声明 | pom.xml | requirements.txt |
| 依赖仓库 | Maven Central | PyPI |
Python 依赖管理示例:
# 安装单个包
pip install requests
# 安装指定版本
pip install requests==2.28.0
# 升级包
pip install --upgrade requests
# 卸载包
pip uninstall requests
# 导出依赖
pip freeze > requirements.txt
# 安装依赖
pip install -r requirements.txt
10.9 标准库
Java 标准库
java.lang:核心类java.util:工具类和集合java.io:IO 操作java.net:网络操作java.math:数学运算java.time:日期时间
Python 标准库
os:操作系统接口sys:Python 解释器相关math:数学函数datetime:日期时间collections:集合扩展json:JSON 处理re:正则表达式urllib:URL 处理socket:网络编程threading:多线程multiprocessing:多进程
对比:
| 功能 | Java | Python |
|---|---|---|
| 文件操作 | java.io | os, os.path |
| 网络操作 | java.net | socket, urllib |
| 日期时间 | java.time | datetime |
| 正则表达式 | java.util.regex | re |
| 数学运算 | java.math | math |
| 集合操作 | java.util | collections |
| JSON 处理 | javax.json | json |
10.10 练习
- 模块创建练习:创建一个包含数学工具函数的模块
- 包创建练习:创建一个包含多个子模块的包
- 导入练习:练习不同的导入方式(导入整个模块、导入特定成员、使用别名)
- 相对导入练习:在包内使用相对导入
- 依赖管理练习:使用 pip 安装第三方库并创建 requirements.txt 文件
- 标准库练习:使用 Python 标准库完成一个小任务(如文件操作、日期处理)
10.11 小结
- Python 的模块系统比 Java 更简单,一个 .py 文件就是一个模块
- Python 的包通过
__init__.py文件标识,而 Java 通过 package 声明 - Python 的导入语法更灵活,支持别名和多种导入方式
- Python 的相对导入使用点号表示级别,比 Java 更直观
- Python 的依赖管理使用 pip,比 Maven/Gradle 更轻量级
- Python 的标准库非常丰富,覆盖了大多数常见任务
- Python 的
if __name__ == "__main__"机制比 Java 的 main 方法更灵活
通过本章的学习,你已经了解了 Java 和 Python 在模块与包上的主要区别。接下来,我们将学习 Python 的高级特性。