一架梯子,一头程序猿,仰望星空!
Mojo教程 > 内容正文

Mojo模块和包


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 *

因此,你实际上可以通过命名包来导入 functionalreduction 中的任何内容。也就是说,你可以在导入语句中省略 functional 名称,同样有效:

from algorithm import map