前言
在使用fairseq
进行model的注册时,发现对python文件中的 __init__.py
文件的原理不清晰,导致注册失败。在这里记录一下。
一、__init__.py
是什么?
__init__.py
文件是Python包(或目录)中的特殊文件,用于标识包目录,并在包被导入时执行初始化操作。它可以包含包级别的变量、函数和类,也可以用于指定包的子模块或子包。
当导入一个包时,init.py 文件会在包的初始化过程中执行。在其中可以执行任何必要的初始化操作,例如设置包的全局变量,导入子模块,或者执行其他自定义操作。这使得包的组织和初始化更加方便。
比如:以下目录结构中
my_package/
|___init__.py
|_ module1.py
|_ module2.py
__init__.py
可以是以下内容:
# my_package/__init__.py
print("Initializing my_package...")
# 导入子模块
from . import module1
from . import module2
# 定义包级别的变量
package_variable = "This is a package variable"
当导入 my_package 时,__init__.py
文件中的代码将被执行:
import my_package
输出 “Initializing my_package…” 并使 my_package 可用于导入包中的子模块和访问包级别的变量。
__init__.py
文件可以为空,但通常会包含有用的初始化代码。这对于组织代码、设置包的默认配置以及执行导入操作非常有用。
二、模块、包和命名空间
在使用__init__.py
之前,有必要理解什么是module、package、namespace。
1.module(模块)
模块是包含Python代码的单个文件。文件的名称通常与模块的名称相同,但具有 .py
扩展名。模块可以包含变量、函数、类以及可执行的代码。
其他Python程序可以使用 import
语句导入模块,以便重用其中定义的功能。
例如,名为 my_module.py
的模块包含了一个函数 my_function
,那么其他程序可以使用以下方式导入并调用该函数:
import my_module
my_module.my_function()
在模块内部,通过全局变量
__name__
可以获取模块名(即字符串)
而python my_module.py <arguments>
这项操作将执行模块里的代码,和导入模块一样,但会把__name__
赋值为 “__main__
”。 如果脚本是被直接执行的,即作为主程序运行,那么__name__
的值将被设置为 “__main__
”。
如果脚本是被导入为模块的,那么__name__
的值将被设置为脚本的文件名(不带扩展名)。
__pycache__
:
Python 把模块的编译版缓存在 pycache 目录中,文件名为 module.version.pyc,version对编译文件格式进行编码,一般是 Python 的版本号。
Python在两种情况下不检查缓存。其一,从命令行直接载入模块,只重新编译,不存储编译结果;其二,没有源模块,就不会检查缓存。为了支持无源文件(仅编译)发行版本,编译模块必须在源目录下,并且绝不能有源模块。
——“没有源模块,就不会检查缓存”:当Python导入一个模块时,它通常会检查是否存在已编译的字节码缓存文件(以 .pyc 或 .pyo 为扩展名)。如果有源模块(.py 文件),Python会比较源模块的时间戳和编译后的字节码文件,以确定是否需要重新编译。但如果没有源模块,只有已编译的字节码文件存在,Python将跳过这个检查。
——“为了支持无源文件(仅编译)发行版本,编译模块必须在源目录下,并且绝不能有源模块”:即如果发布一个不包含源代码的Python发行版,只包含已编译的字节码文件,那么这些字节码文件必须位于源代码的目录下。并且,如果发布版本不包含源代码(即 .py 文件),那么你应该确保没有源模块存在,以防止Python尝试重新编译。
2.Package(包)
包是一个包含多个模块的目录,通常包含一个名为 __init__.py
的特殊文件。包可以嵌套,形成包的层次结构,以便更好地组织和管理模块。
使用包的主要目的是避免模块名称冲突,使代码更模块化。
例如,名为 my_package 的包,包含两个模块 module1 和 module2,其他程序可以这样导入其中的模块:
from my_package import module1
from my_package import module2
包是一种用“点式模块名”构造 Python 模块命名空间的方法。例如,模块名
A.B
表示包 A 中名为 B 的子模块。正如模块可以区分不同模块之间的全局变量名称一样,点式模块名可以区分 NumPy 或 Pillow 等不同多模块包之间的模块名称。
两种导入子模块方式:
- 使用
from package import item
时,item 可以是包的子模块(或子包),也可以是包中定义的函数、类或变量等其他名称。import 语句首先测试包中是否定义了 item;如果未在包中定义,则假定 item 是模块,并尝试加载。如果找不到 item,则触发 ImportError 异常。 - 使用
import item.subitem.subsubitem
句法时,除最后一项外,每个 item 都必须是包;最后一项可以是模块或包,但不能是上一项中定义的类、函数或变量。
包中含有多个子包时:
Python 程序的主模块必须始终使用绝对导入,如
from sound.effects import echo
而非from .. import echo
3.Namespace(命名空间)
命名空间是变量名和对象之间的映射,用于解析变量名引用。
Python中存在多个命名空间,包括全局命名空间(global namespace
)和局部命名空间(local namespace
)。
当引用一个变量时,Python首先在局部命名空间中查找,然后在全局命名空间中查找,以确定变量的值。
如:
x = 1 # 在全局命名空间中定义变量
def my_function():
x = 2 # 在局部命名空间中定义变量
print(x) # 首先查找局部命名空间,然后在全局命名空间找到变量 x
print(x)
my_function()
#output:1
# 2
三、__init.py__
的用途
见博客