--- fid: 20251024-203000 title: Init Package Progress tags: init, package --- # Init Package Progress (date: 2025-10-24) ## 怎样才能构成一个包 (原理) 在 Python 中,通常一个目录要被识别为一个包(package),需要在该目录中包含一个 `__init__.py` 文件。这个文件可以是空的,但它的存在是必要的,因为它告诉 Python 解释器这个目录是一个包。 不过,从 Python 3.3 开始,引入了一个新的特性,即“隐式命名空间包”(Implicit Namespace Packages)。这种类型的包不需要 `__init__.py` 文件,但有一些限制和要求: 1. **目录结构**:每个目录(包)必须包含一个 `__init__.py` 文件,或者至少包含一个 Python 文件或子目录。 2. **PEP 420**:Python 的 PEP 420 定义了命名空间包的规范,要求包目录必须包含 `__init__.py` 文件,或者至少包含一个 Python 文件或子目录。 因此,如果你的子目录中不包含 `__init__.py` 文件,但包含至少一个 Python 文件或子目录,并且你使用的是 Python 3.3 或更高版本,那么这个子目录仍然可以被识别为一个包。 ## 实验说明 这段笔记记录了一个关于Python模块导入机制的实验,主要目的是: 1. **测试不同包结构的导入方式** - 包括常规包、子模块和命名空间包 2. **验证导入路径和工作目录** - 通过`os.getcwd()`检查当前工作目录 3. **演示命名空间包的特性** - `package3`是一个空目录,形成了命名空间包 ### 文件结构 ```python module_test/ ├── package1 │ ├── __init__.py │ └── module1.py ├── package2 │ ├── __init__.py │ └── module2.py ├── package3 # 空目录,形成命名空间包 └── script.py ``` ### module1.py ```python # module_test/package1/module1.py import os def run(): print("module 1 start ...") print(os.getcwd()) print("module 1 end ...") ``` ### module2.py ```python # module_test/package2/module2.py import os def run(): print("module 2 start ...") print(os.getcwd()) print("module 2 end ...") ``` ### script.py ```python # module_test/script.py import os print("script start ...") print("Current working directory:", os.getcwd()) print("script end") # 导入测试 from package1 import module1 from package2 import module2 import package3 # 执行导入的模块功能 module1.run() module2.run() # 显示package3的信息 print("package3 info:", package3) print("package3 __path__:", getattr(package3, '__path__', 'No __path__')) def run1(): print("run1 start ...") print("Current working directory:", os.getcwd()) print("run1 end ...") def run2(): print("run2 start ...") print("Current working directory:", os.getcwd()) print("run2 end ...") if __name__ == '__main__': run1() run2() ``` ### 预期执行结果: ``` script start ... Current working directory: /home/dai/PycharmProjects/module_test script end module 1 start ... /home/dai/PycharmProjects/module_test module 1 end ... module 2 start ... /home/dai/PycharmProjects/module_test module 2 end ... package3 info: package3 __path__: _NamespacePath(['/home/dai/PycharmProjects/module_test/package3']) run1 start ... Current working directory: /home/dai/PycharmProjects/module_test run1 end ... run2 start ... Current working directory: /home/dai/PycharmProjects/module_test run2 end ... ``` ### 关键现象说明: 1. **工作目录一致性**:所有模块中的`os.getcwd()`都显示相同的工作目录,即项目根目录 2. **命名空间包特性**: - `package3`作为空目录(无`__init__.py`)被Python识别为命名空间包 - 输出显示``,表明这是一个命名空间模块 - 命名空间包可以跨多个目录分布,为灵活的包结构提供支持 3. **导入机制**: - 常规包(package1、package2)需要`__init__.py`文件 - 命名空间包(package3)不需要`__init__.py`文件 - 所有导入都成功执行,没有报错 这个实验清晰地展示了Python中不同包类型的导入行为和工作目录的保持一致性。