Перейти к содержанию

Модули и пакеты

Mojo предоставляет систему упаковки, которая позволяет вам организовывать и компилировать библиотеки кода в импортируемые файлы. На этой странице представлены необходимые понятия о том, как организовать ваш код в модули и пакеты (что во многом похоже на Python), и показано, как создать упакованный двоичный файл с помощью команды `mojo package.

Модули Mojo

Чтобы разобраться в пакетах Mojo, вам сначала нужно разобраться в модулях Mojo. Модуль Mojo - это отдельный исходный файл Mojo, содержащий код, пригодный для использования другими файлами, которые его импортируют. Например, вы можете создать модуль для определения структуры, подобной этой:

mymodule.mojo:

struct MyPair:
    var first: Int
    var second: Int

    fn __init__(out self, first: Int, second: Int):
        self.first = first
        self.second = second

    fn dump(self):
        print(self.first, self.second)

Обратите внимание, что в этом коде нет функции main(), поэтому вы не можете выполнить mymodule.mojo. Однако вы можете импортировать это в другой файл с помощью функции main() и использовать ее там.

Например, вот как вы можете импортировать MyPair в файл с именем main.mojo, который находится в том же каталоге, что и mymodule.mojo:

main.mojo:

from mymodule import MyPair

fn main():
    var mine = MyPair(2, 4)
    mine.dump()

В качестве альтернативы вы можете импортировать весь модуль целиком, а затем получить доступ к его элементам по имени модуля. Например:

main.mojo:

import mymodule

fn main():
    var mine = mymodule.MyPair(2, 4)
    mine.dump()

Вы также можете создать псевдоним для импортированного элемента с помощью as, например, следующим образом:

main.mojo:

import mymodule as my

fn main():
    var mine = my.MyPair(2, 4)
    mine.dump()

В этом примере это работает только тогда, когда mymodule.mojo находится в том же каталоге, что и main.mojo. В настоящее время вы не можете импортировать файлы .mojo в качестве модулей, если они находятся в других каталогах. То есть, если только вы не рассматриваете каталог как пакет Mojo, как описано в следующем разделе.

Модуль Mojo может включать в себя функцию main() и также может быть исполняемым, но обычно это не практикуется, и модули обычно включают API, которые можно импортировать и использовать в других программах Mojo.

Пакеты Mojo

Пакет Mojo - это просто набор модулей Mojo в каталоге, который содержит файл __init__.mojo. Объединив модули в каталоге, вы можете импортировать все модули вместе или по отдельности. При желании вы также можете скомпилировать пакет в файл .mojopkg или .📦, который проще использовать совместно и который по-прежнему совместим с другими системными архитектурами.

Вы можете импортировать пакет и его модули либо непосредственно из исходных файлов, либо из скомпилированного файла .mojopkg/.📦. Для Mojo не имеет особого значения, каким способом вы импортируете пакет. При импорте из исходных файлов имя каталога используется как имя пакета, тогда как при импорте из скомпилированного пакета именем файла является имя пакета (которое вы указываете с помощью команды mojo package — оно может отличаться от имени каталога).

Например, рассмотрим проект с этими файлами:

main.mojo:

mypackage/
    __init__.mojo
    mymodule.mojo

mymodule.mojo - это тот же код, что и в примерах выше (со структурой MyPair), а __init__.mojo пуст.

Файл __init__.mojo необходим. Если у вас его нет, Mojo не распознает каталог как пакет, и вы не сможете импортировать mymodule.

В этом случае основной файл main.mojo теперь может импортировать MyPair через имя пакета следующим образом:

main.mojo:

from mypackage.mymodule import MyPair

fn main():
    var mine = MyPair(2, 4)
    mine.dump()

This immediately works:

mojo main.mojo
2 4

Однако, если вы не хотите, чтобы исходный код mypackage находился в том же месте, что и main.mojo, вы можете скомпилировать его в файл пакета, подобный этому:

mojo package mypackage -o mypack.mojopkg

Файл .mojopkg содержит незавершенный код, поэтому вы можете использовать его совместно в разных системах. Код становится исполняемым файлом, зависящим от архитектуры, только после того, как он импортируется в программу Mojo, которая затем компилируется с помощью mojo build.

Теперь вы можете переместить исходный код mypackage в другое место, и файлы проекта теперь будут выглядеть следующим образом:

main.mojo
mypack.mojopkg

Поскольку мы назвали пакет mypack, нам нужно исправить инструкцию import:

main.mojo:

from mypack.mymodule import MyPair

И код работает точно так же:

mojo main.mojo
2 4

Если вы хотите переименовать свой пакет, вы не можете просто отредактировать имя файла .mojopkg или .📦, поскольку имя пакета закодировано в файле. Вместо этого вы должны снова запустить mojo package, чтобы указать новое имя.

__init__ файл

Как упоминалось выше, файл __init__.mojo необходим для указания того, что каталог следует рассматривать как пакет Mojo, и он может быть пустым.

В настоящее время в файлах .mojo не поддерживается код верхнего уровня, поэтому, в отличие от Python, вы не можете написать код в __init__.mojo, который выполняется при импорте. Однако вы можете добавить структуры и функции, которые затем можно импортировать из имени пакета.

Однако, вместо добавления API-интерфейсов в файл __init__.mojo, вы можете импортировать элементы модуля, что дает тот же эффект, делая ваши API-интерфейсы доступными из имени пакета, вместо того, чтобы требовать обозначения <package_name>.<module_name>.

Например, опять же, предположим, что у вас есть эти файлы:

main.mojo:

mypackage/
    __init__.mojo
    mymodule.mojo

Давайте теперь добавим следующую строку в __init__.mojo:

__init__.mojo
from .mymodule import MyPair

Это все, что там есть. Теперь мы можем упростить инструкцию import в main.mojo следующим образом:

main.mojo:

from mypackage import MyPair

Эта функция объясняет, почему некоторые элементы стандартной библиотеки Mojo могут быть импортированы из их имени пакета, в то время как для других требуется обозначение <package_name>.<module_name>. Например, функциональный модуль находится в пакете algorithm, поэтому вы можете импортировать элементы этого модуля (например, функцию map()) следующим образом:

from algorithm.functional import map

Однако файл algorithm/__init__.mojo также содержит эти строки:

algorithm/__init__.mojo
from .functional import *
from .reduction import *

Таким образом, вы действительно можете импортировать что угодно из functional или reduction, просто присвоив пакету имя. То есть вы можете удалить название функции из инструкции import, и это также работает:

from algorithm import map

Какие модули из стандартной библиотеки импортируются в область действия пакета, зависит от области применения и может быть изменено. Обратитесь к документации по каждому модулю, чтобы узнать, как можно импортировать его элементы.