Python 모듈/파일 import 방법들에 대하여

Python 모듈/파일 import 방법들에 대하여

들어가며

Python으로 프로젝트를 진행하면, 코드가 길어지기 시작하고 코드가 너무 길어지면 코드의 가독성이 매우 떨어지게 됩니다. 추가적으로, 작성한 여러 함수들이 비슷한 목적을 가지고 있다면, 이들을 그룹화하거나 같이 관리하는 등 우리에게 익숙한 개념인 "모듈화"가 필요하다고 느껴지게 됩니다. 코드를 여러 파일로 분리하고 분리한 코드들을 다시 필요한곳에서 사용하는 방법인 모듈화와 import에 대하여 알아보겠습니다.

Python의 모듈(Module)

Python에서의 모듈은 다른 Python 코드로 import(코드를 불러와서 사용하는 것)할 수 있는 Python 코드의 단일 단위입니다. 모듈에는 정의(함수 및 상수의 정의)와 이러한 정의를 초기화하는 초기화문이 포함됩니다. 이러한 모듈이 작성되면 모듈을 import하는 모든 스크립트에서 재사용이 가능합니다.

Python 모듈을 만드는 가장 쉬운 방법은 .py(python 확장자)로 끝나는 파일을 만들고 그 안에 Python 코드를 쓰는 것입니다. 하지만, 파일명에 다른 파일 확장자를 사용하거나 확장자를 전혀 사용하지 않을 경우 표준 import 문은 작동하지 않습니다. 이 상황에서 import를 사용하려면 importlib라는 라이브러리를 사용해야 합니다.

위에서 언급한 표준 import문을 사용할 수 있는 모듈은 아래처럼 표현할 수 있습니다.

# root/modulesample.py

def module_sample_function():
  print("I'm imported!")

위 모듈을 포함한 module-sample.py를 생성한 후, 이제 Python 스크립트에서 화면에 "I'm imported!"라고 프린트하고 싶을 때마다 메시지를 다시 쓸 필요 없이 이 모듈을 import하여 사용할 수 있습니다. 또한 만약 이 기능을 사용하는 많은 Python코드에서 표시하는 메시지(I'm imported!)를 모두 I'm exported!로 변경하여야 할 때 여러 스크립트에서 모든 I'm imported를 바꾸는 것이 아닌 module-sample.py 내의 module_sample_function 함수의 코드 한 줄을 변경하여 목표를 완료할 수 있습니다.

같은 디렉토리에 있는 파일 import 하기

아래와 같은 python 프로젝트가 있다고 가정합니다.

📁 root
 ├─ 📄 main.py
 └─ 📄 modulesample.py

module-sample.py는 위에서 표현한 module_sample_function함수가 정의되어있고, main.py에서 해당 함수를 불러서 사용하고 싶다면 아래의 코드처럼 사용할 수 있습니다.

# root/main.py
import modulesample

modulesample.module_sample_function()

import만으로 모듈을 불러올때는 파일명에서 .py확장자만 제거한 이름을 import를 앞에 붙여 사용하시면 됩니다.

하위 디렉토리에 존재하는 파일 import 하기

파이썬 버전 3.3 이상을 사용하신다면, 현재위치한 파일의 디렉터리의 하위 디렉터리에 있는 모듈을 쉽게 가져올 수 있습니다. 반면, 3.3 이전 버전의 Python을 사용하신다면, 아래글에서 이어나갈 "다른 디렉토리에 존재하는 파일 import 하기"를 참고하시기를 바랍니다.

아래와 같은 python 프로젝트가 있다고 가정합니다.

📁 root
 ├─ 📄 main.py
 └─ 📁 subdirectory
    └─ 📄 modulesample.py

main.py에서 위에서 정의한 모듈을 아래처럼 사용할 수 있습니다.

# root/main.py

import subdirectory.moduelsample

subdirectory.moduelsample.module_sample_function()

Python에서 import문에서는 .을 사용하여 경로를 구분합니다.

import-as문을 이용하면, 더 간결한 모듈명으로 할당하여 사용할 수 있습니다.

import subdirectory.moduelsample as short_name

short_name.module_sample_function()

아래처럼 모듈을 직접 import하는 방법도 존재합니다.

from subdirectory.moduelsample import module_sample_function

module_sample_function()

다른 디렉토리에 존재하는 파일 import 하기

아래와 같은 python 프로젝트가 있다고 가정합니다.

📁 root
 ├─ 📁 level1
 │  └─ 📁 level2
 │     └─ 📄 modulesample.py
 └─ 📁 level1-1
    └─ 📄 main.py

기본적으로 Python은 sys.path에 정의된 다른 표준 위치뿐만 아니라 현재 스크립트 파일과 동일한 디렉터리(하위 디렉터리 포함)에서 파일을 찾습니다. 이러한 위치가 궁금하다면 다음과 같이 sys.path 변수를 출력해 볼 수 있습니다.

import sys

for path in sys.path:
  print(path)

하지만, import하려는 파일이 완전히 다른 곳에 있는 경우 먼저 sys.path에 검색 디렉터리를 추가하여 Python에게 검색 위치를 알려주어야 합니다. 아래의 예제처럼 import하면 위에서 정의했던 모듈을 사용하라 수 있습니다.

import sys

sys.path.append("/level1/level2")
import modulesample

modulesample.module_sample_function()

한가지 더 알아야하는 포인트는 sys.path에 추가된 경로는 절대경로라는 것 입니다. 만약, 상대경로를 사용하면 script.py의 경로에 상대적인 경로가 아니라 사용자가 스크립트를 실행하는 디렉토리에 따라 경로가 다르게 해결됩니다. 다른 말로하면, 사용자가 현재 위치한 디렉토리가 기준이 되고 모든경로를 해당 디렉토리의 상대적으로 경로를 설정해야 합니다.

위를 더 쉽게 표현하면 main.py가 위치한 level-1에서 main.py를 실행한다면, 바로 위의 코드는 level1-1에서 level1/level2를 찾을 것 입니다. 하지만 우리가 가정한 디렉토리 구조에서는 level1-1에는 해당 디렉토리가 존재하지 않으며, 상위 디렉토리인 루트 하위에 존재합니다.

사용자의 위치가 어디서나 main.py 파일에 상대적인 디렉토리를 추가하려면 __file__을 사용하여 현재 main.py의 전체 경로를 가져오고 그곳에서 import에 대한 경로를 만들 수 있습니다. main.py에서 다음과 같이 쓸 수 있습니다.

import os
import sys

script_dir = os.path.dirname(__file__)
mymodule_dir = os.path.join( script_dir, '..', 'level1', 'level1' )
sys.path.append(mymodule_dir)
import modulesample

modulesample.module_sample_function()

py확장자가 아닌 파일을 포함한 모든 파일 import하기

Python 버전 3.4 이상에서는 파일의 확장자가 .py가 아닌 경우에도 모든 파일을 Python 모듈로 로드할 수 있는 내장 importlib 라이브러리를 통해 기능을 제공합니다.

아래와 같은 python 프로젝트가 있다고 가정합니다.

📁 root
 ├─ 📁 level1
 │  └─ 📁 level2
 │     └─ 📄 modulesample
 └─ 📁 level1-1
    └─ 📄 main.py

위 예제에는 modulesample 파일에 파일 확장자가 없습니다. 이런 경우 단순한 import 문을 사용하여 해당 파일을 가져올 수 없습니다. 하지만, 아래의 방법으로 문제를 해결할 수 있습니다. 아래의 예제 또한 파일에 대한 상대적인 경로를 사용하여 사용자의 위치와 관계없이 import가 가능하도록 작성되었습니다.

import importlib.machinery
import importlib.util
from pathlib import Path

script_dir = Path(__file__).parent
modulesample_path = str(script_dir.joinpath( '..', 'level1', 'level2', 'modulesample'))

loader = importlib.machinery.SourceFileLoader('modulesample', modulesample_path)
spec = importlib.util.spec_from_loader('modulesample', loader)
modulesample = importlib.util.module_from_spec(spec)
loader.exec_module(modulesample)

modulesample.module_sample_function()

마치며

모듈화와 import 방법들은 여러 프로그래밍 언어를 이용하여 프로젝트를 진행할때, 매우 기본적으로 진행해야 하는 파트입니다. 모듈화와 import를 제대로 적용하지 못하면, 매우 길어지는 코드와 특정 목적을 달성하는 코드가 어디에 위치하는지 알아내는 것이 매우 어려워져, 프로젝트를 진행하는데 매우 큰 어려움을 겪을것입니다.
python은 스크립트 언어로서, python만의 방식들이 존재하고 이에대하여 알아보았습니다. 위에서 알아본 내용들을 현재 진행중이신, 또 앞으로 만나시게될 프로젝트들에서 도움이 되시길 바라며 글을 마치겠습니다.

목록으로 돌아가기 >