Mojo模块和包
Mojo提供了一种打包系统,允许将代码库组织和编译为可导入的文件。本章介绍了如何将代码组织为模块和包的必要概念(类似于Python),并演示了如何使用mojo package
命令创建打包后的二进制文件。
Mojo模块
要了解Mojo包,首先需要了解Mojo模块。Mojo模块是包含其他文件可以导入并使用的代码的单个Mojo源文件。例如,您可以创建一个定义结构体的模块,如下所示:
mymodule.mojo
struct MyPair:
var first: Int
var second: Int
fn __init__(inout self, first: Int, second: Int):
self.first = first
self.second = second
fn dump(self):
print(self.first, self.second)
注意,该代码没有main()
函数,所以无法执行mymodule.mojo
。但是,您可以在具有main()
函数的另一个文件中导入它并在那里使用。
例如,以下是如何在与mymodule.mojo
位于同一目录的名为main.mojo
的文件中导入MyPair
:
main.mojo
from mymodule import MyPair
fn main():
let mine = MyPair(2, 4)
mine.dump()
或者,您可以导入整个模块,然后通过模块名访问其成员。例如:
main.mojo
import mymodule
fn main():
let mine = mymodule.MyPair(2, 4)
mine.dump()
还可以使用as
为导入的成员创建别名,如下所示:
main.mojo
import mymodule as my
fn main():
let mine = my.MyPair(2, 4)
mine.dump()
在此示例中,只有当mymodule.mojo
位于与main.mojo
相同的目录中时才起作用。目前,如果.mojo
文件位于其他目录中,则无法将其作为模块导入。除非将该目录视为一个Mojo包,这将在下一部分中进行描述。
注意: Mojo模块可能包含一个main()
函数,也可能是可执行的,但这通常不是实践的做法,模块通常包含要导入和在其他Mojo程序中使用的API。
Mojo包
Mojo包只是一个包含__init__.mojo
文件的目录中的Mojo模块集合。通过将模块组织在一个目录中,您可以一起或单独导入所有模块。可选地,您还可以将包编译成更容易共享的.mojopkg
或.📦
文件。
您可以直接从源文件或编译后的.mojopkg
/.📦
文件导入包和其模块。对于Mojo来说,无论以哪种方式导入包,其实际差异不大。当从源文件导入时,目录名作为包名,而当从编译后的包导入时,文件名就是包名(您可以使用mojo package
命令指定它,它可以与目录名不同)。
例如,考虑一个具有以下文件的项目:
main.mojo
mypackage/
__init__.mojo
mymodule.mojo
mymodule.mojo
是上面示例中的相同代码(包括MyPair
结构)和__init__.mojo
文件为空。
在这种情况下,main.mojo
文件现在可以通过包名导入MyPair
,如下所示:
from mypackage.mymodule import MyPair
fn main():
let mine = my.MyPair(2, 4)
mine.dump()
请注意,__init__.mojo
在此处至关重要。如果删除它,则Mojo不会将目录识别为包,并且无法导入mymodule
。
然后,假设您不希望mypackage
源代码与main.mojo
位于相同位置。因此,您可以将其编译为一个包文件,如下所示:
mojo package mypackage -o mypack.mojopkg
然后,mypackage
源代码可以移动到其他位置,项目文件现在如下所示:
main.mojo
mypack.mojopkg
因为我们将包文件命名为与目录不同的名字,所以需要修复导入语句,一切仍然正常工作:
from mypack.mymodule import MyPair
注意:如果要重命名包,不能单纯地编辑.mojopkg
或.📦
文件的名称,因为包名已经编码在文件中。您必须再次运行mojo package
来指定新的名称。
__init__
文件
如上所述,__init__.mojo
文件是必需的,用于指示一个目录应该被视为一个 Mojo 包,并且它可以是空的。
目前,.mojo
文件中不支持顶级代码,因此与 Python 不同,你不能在 __init__.mojo
中编写在导入时执行的代码。但是,你可以添加结构体和函数,然后从包名中导入它们。
然而,你可以在 __init__.mojo
文件中导入模块成员,通过这种方式可以使你的 API 从包名中可访问,而不需要使用 .
符号。
例如,假设你有以下文件:
main.mojo
mypackage/
__init__.mojo
mymodule.mojo
现在在 __init__.mojo
文件中添加以下行:
__init__.mojo
from .mymodule import MyPair
这就是 __init__.mojo
文件中的内容。现在,我们可以简化 main.mojo
中的导入语句,像这样:
main.mojo
from mypackage import MyPair
这个特性解释了为什么 Mojo 标准库中的某些成员可以从包名中导入,而其他成员需要使用 .
符号。例如,functional
模块位于 algorithm
包中,因此你可以像这样导入该模块的成员(例如 map()
函数):
from algorithm.functional import map
然而,algorithm/__init__.mojo
文件还包括以下代码:
algorithm/__init__.mojo
from .functional import *
from .reduction import *
因此,你实际上可以通过命名包来导入 functional
或 reduction
中的任何内容。也就是说,你可以在导入语句中省略 functional
名称,同样有效:
from algorithm import map