2.19. 모듈 작성하기

모든 .py 파일은 모듈입니다. 커지는 스크립트를 여러 파일로 나누면 각 파일이 짧게 유지되고, 공통 헬퍼를 스크립트들 사이에서 공유할 수 있습니다.

2.19.1. 스크립트 나누기

관련 있는 함수 묶음을 별도의 파일로 빼내세요:

camera_utils.py

def banner():
    print("OpenMV")

def label(text):
    return "[" + text + "]"

main.py

import camera_utils

camera_utils.banner()
print(camera_utils.label("ready"))

출력:

OpenMV
[ready]

두 파일은 같은 디렉터리에 나란히 놓입니다. main.py 가 실행될 때 import camera_utilscamera_utils.py 를 한 번 읽고, 그 최상위 문장들을 실행한 다음, 그 결과로 만들어진 모듈 객체를 main 안의 camera_utils 이름에 바인딩합니다. 다른 어디서든 두 번째 import camera_utils 를 하면 동일한 객체가 반환됩니다. 모듈은 처음 로드된 후 캐시되기 때문입니다.

모듈의 이름은 그 파일명에서 오므로, camera_utils.pyimport camera_utils 로 가져옵니다.

2.19.2. 다중 파일 모듈(패키지)

모듈은 단일 .py 가 아니라 파일들의 디렉터리 일 수도 있습니다. 디렉터리 이름이 모듈 이름이 되고, 그 안의 파일들이 서브모듈 이 됩니다:

camera_utils/
    __init__.py
    text.py
    timing.py

__init__.py 는 패키지 자체가 임포트될 때 실행되는 파일입니다. 비어 있어도 되고, 서브모듈에서 선택한 이름들을 다시 내보낼 수도 있습니다. 서브모듈은 점으로 구분된 이름으로 접근합니다:

import camera_utils.text
from camera_utils.timing import elapsed

camera_utils.text.label("ready")

패키지 안에서 서브모듈끼리는 전체 점 구분 이름으로 서로에게 접근하거나, “이 패키지”를 뜻하는 선행 점을 사용하는 상대 임포트 로 접근할 수 있습니다:

camera_utils/timing.py

from . import text             # the sibling submodule

def stamp(value):
    return text.label(str(value))

대신 특정 이름을 가져오려면, 점으로 구분된 형제 모듈 뒤에 그 이름을 적습니다:

from .text import label

def stamp(value):
    return label(str(value))

상대 임포트는 패키지를 자체 완결적으로 유지합니다. 패키지 디렉터리의 이름을 바꿔도 모든 서브모듈을 수정할 필요가 없습니다.

단일 파일이 편안한 크기를 넘어 커지거나, 서로 관련된 모듈 집합이 하나의 네임스페이스 아래에 함께 속해야 할 때 패키지를 사용하세요. 일상적인 스크립트에는 단일 .py 파일로 충분합니다.

2.19.3. __name__ 가드

모든 모듈은 내장 이름 __name__ 을 가집니다. 그 값은 파일이 어떻게 사용되는지에 따라 달라집니다:

  • 파일을 직접 실행 하면 __name__ 은 문자열 "__main__" 으로 설정됩니다.

  • 파일이 다른 스크립트에 의해 임포트 되면 __name__ 은 모듈 이름, 즉 .py 를 뺀 파일명으로 설정됩니다.

이것을 사용하는 관용구는 다음과 같습니다:

label_util.py

def label(text):
    return "[" + text + "]"

if __name__ == "__main__":
    print(label("self-test"))

출력(직접 실행할 때):

[self-test]

같은 파일이 대신 임포트 되면 __name__ 은 모듈 이름으로 설정되므로, if 블록은 건너뛰고 추가로 실행되는 것이 없습니다:

>>> import label_util
>>> label_util.__name__
'label_util'

이 패턴을 사용하면, 라이브러리 파일을 임포트하는 스크립트를 방해하지 않으면서 라이브러리 파일에 간단한 스모크 테스트나 데모를 붙일 수 있습니다.