AI/기타

[Mojo] 모듈과 패키지

sangwonYoon 2023. 9. 24. 16:29

이번 포스팅에서는 Mojo로 작성한 코드를 모듈과 패키지로 만드는 방법에 대해 알아보자.


Mojo 모듈

Mojo 모듈은 다른 파일에서 import하여 사용할 수 있는 단일 소스 파일이다.

예를 들어, 아래와 같이 구조체를 정의하는 모듈을 만들 수 있다.

# 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)

 

mymodule MyPair 구조체를 import하기 위해서는 아래와 같이 코드를 작성한다.

# main.mojo
from mymodule import MyPair

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

이 때, main.mojo mymodule.mojo는 같은 디렉토리에 위치해야 한다.

 

또는 모듈 전체를 import한 뒤, 모듈의 구성 요소에 접근하는 방식도 가능하다.

# main.mojo
import mymodule

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

 

또한 import한 모듈에 as 키워드를 사용하여 별칭을 붙일 수 있다.

import mymodule as my

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

 

현재, Mojo에서 다른 디렉토리에 있는 .mojo 파일을 모듈로 import하기 위해서는 해당 디렉토리를 Mojo 패키지로 만들어야 한다. 그렇다면 디렉토리를 Mojo 패키지를 만드는 방법에 대해서 알아보자.

 

Mojo 패키지

Mojo 패키지는 init.mojo 파일과 함께 Mojo 모듈을 디렉토리에 모아놓은 것이다.

Mojo 패키지는 .mojopkg 또는 .📦 파일로 컴파일하여 손쉽게 공유할 수도 있다.

 

Mojo 패키지를 import하는 방식은 소스 파일을 import하거나 .mojopkg 또는 .📦 파일을 import하는 방식 중 선택할 수 있고, 두 방식 모두 패키지를 사용하는 방식에는 차이가 없다.

소스 파일을 import하는 경우, 디렉토리 이름이 패키지의 이름으로 사용된다.

컴파일된 패키지를 import하는 경우, 파일 이름이 패키지의 이름으로 사용된다. (컴파일된 패키지 파일의 이름은 mojo package 명령어로 지정할 수 있다.)

 

소스 파일을 import

예를 들어, 아래와 같은 파일 구조의 프로젝트를 가정해보자.

main.mojo
mypackage/
    __init__.mojo
    mymodule.mojo

 

mymodule.mojo에는 MyPair 구조체가 작성되어 있다.

# 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)

__init__.mojo 파일은 비어 있다.

 

이 경우, main.mojo에서 MyPair 구조체를 사용하기 위해서는 아래와 같이 코드를 작성해야 한다.

from mypackage.mymodule import MyPair

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

 

__init__.mojo 파일은 해당 디렉토리가 패키지임을 알려주는 역할을 한다. 따라서 만약 __init__.mojo 파일을 삭제할 경우, Mojo가 디렉토리를 패키지로 인식하지 못하게 되어 mymodule을 import할 수 없게 된다.

 

컴파일된 패키지 파일을 import

이번에는 패키지를 컴파일하여 import하는 방법에 대해 알아보자. mypackage 패키지를 mypack.mojopkg 파일로 컴파일하기 위해서는 다음과 같은 명령어를 사용한다.

mojo package mypackage -o mypack.mojopkg

 

이제 프로젝트의 파일 구조를 아래와 같이 바꿀 수 있다.

main.mojo
mypack.mojopkg

 

이 때, 패키지 파일의 이름이 디렉토리의 이름과 달라졌기 때문에 import문을 아래와 같이 수정해야 한다. 

# main.mojo
from mypack.mymodule import MyPair

 

패키지 파일 이름 변경 시 주의 사항

패키지 파일의 이름을 바꾸고 싶은 경우, 단순히 .mojopkg 또는 .📦 파일의 이름을 수정해서는 안된다. 파일 내부에 패키지의 이름이 인코딩되어 있기 때문에 반드시 mojo package 명령어를 통해 새로운 이름으로 패키지 파일을 생성해야 한다.

 

__init__ 파일

앞에서 언급한 바와 같이 init.mojo 파일은 해당 디렉토리가 패키지임을 나타내는 파일이다. 또한, 이 파일은 비어 있을 수 있다.

파이썬과 달리 현재 Mojo는 top-level code를 지원하지 않기 때문에, 패키지가 import될 때 실행되는 코드를 작성할 수 없다.

top-level code란?
함수 본문이나 클래스 내부에 적혀져 있지 않은 실행 가능한 코드를 의미한다.

 

init.mojo 파일에 패키지를 구성하고 있는 모듈을 import하여 외부에서 패키지의 모듈을 import할 때 <패키지_이름>.<모듈_이름>으로 표기하는 대신, 모듈 이름을 생략하고 <패키지_이름>만으로 모듈에 접근할 수 있다.

예를 들어, 파일 구조가 아래와 같을 때

main.mojo
mypackage/
    __init__.mojo
    mymodule.mojo

__init__.mojo 파일에 아래와 같은 내용을 추가해보자.

# __init__.mojo
from .mymodule import MyPair

그렇다면 이제 main.mojo에서 mymodule 모듈에 있는 MyPair 구조체를 import하기 위해서는 아래와 같이 작성한다.

# main.mojo
from mypackage import MyPair

 

이 원리는 Mojo의 표준 라이브러리에도 적용되어 있다.

예를 들어, algorithm 패키지의 functional 모듈에 있는 map() 함수를 import하기 위해서는 아래와 같이 작성해야 한다.

from algorithm.functional import map

그런데 algorithm/__init__.mojo 파일에는 아래와 같은 코드가 존재한다.

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

따라서 functional과 reduction 모듈에 있는 모든 구성 요소들을 모듈의 이름을 생략하여 import할 수 있다. 따라서 map() 함수를 import하는 코드를 아래와 같이 간소화할 수 있다.

from algorithm import map