模块与包
一个 .py 文件就是一个模块,一个目录就是一个包。import 会真的执行代码,这点和 Java 不同。
模块和 import
每个 .py 文件都是一个模块,模块名就是文件名。导入后,模块里的函数、类和变量都可以通过模块名访问。
导入
import math
from math import sqrt, pi
from datetime import date as Day
print(math.floor(2.7)) # => 2
print(sqrt(16)) # => 4.0
print(Day(2026, 1, 1)) # => 2026-01-01import java.time.LocalDate;
import static java.lang.Math.sqrt;
System.out.println(Math.floor(2.7)); // => 2.0
System.out.println(sqrt(16)); // => 4.0
System.out.println(
LocalDate.of(2026, 1, 1)); // => 2026-01-01import kotlin.math.floor
import kotlin.math.sqrt
import java.time.LocalDate as Day
println(floor(2.7)) // => 2.0
println(sqrt(16.0)) // => 4.0
println(Day.of(2026, 1, 1)) // => 2026-01-01| 对比项 | Python | Java |
|---|---|---|
| 发生时机 | 运行时执行模块代码 | 编译期的名字简写 |
| 导入单位 | 模块,也就是文件 | 类 |
| 别名 | import x as y | 不支持 |
| 导入函数 | from math import sqrt | import static |
| 对比项 | Python | Kotlin |
|---|---|---|
| 发生时机 | 运行时执行模块代码 | 编译期的名字简写 |
| 导入单位 | 模块,也就是文件 | 类、函数或属性 |
| 导入函数 | from math import sqrt | import kotlin.math.sqrt |
| 别名 | 相同import x as y | |
模块顶层不要写耗时操作或副作用。不要用 from x import *,来源不清楚,还容易覆盖名字。
脚本入口
模块被直接运行时,__name__ 的值是 "__main__"。被别的文件导入时,__name__ 是模块名。用它就能让文件既能当脚本运行,又能被导入而不触发运行逻辑。
main 入口
def main():
print("运行中") # => 运行中
if __name__ == "__main__":
main()public class App {
public static void main(String[] args) {
System.out.println("运行中");
}
}fun main() {
println("运行中")
}| 对比项 | Python | Java |
|---|---|---|
| 入口写法 | if __name__ == "__main__": | public static void main |
| 是否必须 | 可选 | 必须 |
| 被其他文件引用时 | 顶层代码会执行,这个分支不会 | 入口不执行 |
| 对比项 | Python | Kotlin |
|---|---|---|
| 入口写法 | if __name__ == "__main__": | fun main() |
| 是否必须 | 可选 | 必须 |
| 被其他文件引用时 | 顶层代码会执行,这个分支不会 | 入口不执行 |
把逻辑放进 main() 函数,只在底部调用,这样其他模块和测试都能安全地导入这个文件。
模块搜索路径
import 按 sys.path 里的目录依次查找,相当于类路径。顺序是脚本所在目录、PYTHONPATH 环境变量、标准库、site-packages(第三方包)。
__pycache__、dir() 和标准模块选读
Python 把编译好的字节码缓存在 __pycache__/模块名.cpython-312.pyc,源文件改动后自动更新,这个目录应该写进 .gitignore。内置函数 dir(模块) 列出模块里的所有名字,在交互模式下查 API 很方便。help(模块) 显示文档。
在交互模式下改了模块后,再次 import 不会生效,要调用 importlib.reload(模块)。包的 __path__ 属性决定到哪些目录里找子模块,很少需要改动。
包
包是包含模块的目录,通常放一个 __init__.py。这个文件在包第一次导入时执行,可以是空文件,也可以用来导出公开 API。
文本
shop/
├── __init__.py
├── utils.py
└── orders/
├── __init__.py
└── service.py包和导入路径
# shop/orders/service.py
from shop.utils import money # 绝对导入
from ..utils import money # 相对导入
# shop/__init__.py,导出公开 API
from .orders.service import place_order
__all__ = ["place_order"]// shop/orders/Service.java
package shop.orders;
import shop.utils.Money;
public class Service {
public void placeOrder() { }
}// shop/orders/Service.kt
package shop.orders
import shop.utils.money
fun placeOrder() { }| 对比项 | Python | Java |
|---|---|---|
| 包名来源 | 目录结构 | package 语句 |
| 目录要求 | 目录就是包 | 必须和 package 一致 |
| 相对导入 | from ..utils import x | 不支持 |
| 可见性 | 默认公开,_name 是约定 | 访问修饰符 |
| 包初始化 | __init__.py | 没有 |
| 对比项 | Python | Kotlin |
|---|---|---|
| 包名来源 | 目录结构 | package 语句 |
| 目录要求 | 目录就是包 | 可以不一致 |
| 相对导入 | from ..utils import x | 不支持 |
| 可见性 | 默认公开,_name 是约定 | 访问修饰符 |
| 包初始化 | __init__.py | 没有 |
公开 API 通常在 __init__.py 里集中导出,__all__ 决定 import * 导出什么。
| Python | Java | Kotlin | 说明 |
|---|---|---|---|
模块(.py 文件) | 类文件 | 文件 | Python 的最小导入单位是文件 |
| 包(目录) | package | package | |
sys.path | classpath | classpath | |
__init__.py | 无 | 无 | 包初始化代码 |
__all__ | public | public / internal | Python 里只影响 import * |
_name | private / 包私有 | private / internal | 约定,不强制 |
本章要点
- 文件即模块,目录即包,包名由目录结构决定
import在运行时执行模块顶层代码,只执行一次- 脚本入口统一写
if __name__ == "__main__": - 别用
from x import *,别和标准库模块重名 __init__.py负责包初始化和导出公开 API
官方原文:模块