Computer >> 컴퓨터 >  >> 프로그래밍 >> Python

Python importlib 완벽 가이드: 동적 모듈 임포트 구현하기

importlib 패키지는 Python 소스 코드로 작성된 import 문의 구현을 제공하며, 어떤 Python 인터프리터에서도 이식 가능하다는 특징이 있습니다. 또한 Python이 아닌 다른 프로그래밍 언어로 구현된 임포트 메커니즘보다 훨씬 이해하기 쉽습니다.

이 패키지는 임포트를 구현하는 데 필요한 구성 요소들을 공개하기 때문에, 개발자가 자신만의 커스텀 객체(일명 임포터(importer))를 만들어 임포트 과정에 직접 참여시키는 것도 손쉽게 할 수 있습니다.

importlib 패키지에는 import_module()이라는 핵심 함수가 있으며, 그 외에도 유용한 함수들을 제공합니다.

주요 함수 살펴보기

import_module()

이 함수는 프로그래밍 방식으로 모듈을 임포트합니다. 첫 번째 매개변수는 모듈 이름이며, 선택 사항인 두 번째 매개변수로 패키지 이름(존재하는 경우)을 지정할 수 있습니다.

invalidate_caches()

이 함수는 파인더(finder)들의 내부 캐시를 무효화합니다. 프로그램 실행 중에 새 모듈이 생성되거나 설치되었을 때, 모든 파인더가 해당 모듈의 존재를 확실히 인식하도록 하려면 반드시 이 함수를 호출해야 합니다.

reload()

이 함수는 이전에 임포트했던 모듈을 다시 로드합니다. 외부 편집기로 모듈의 소스 파일을 수정한 뒤, Python 인터프리터를 종료하지 않고도 변경된 버전을 바로 테스트하고 싶을 때 특히 유용합니다.

동적 임포트 실습 예제

먼저 module1.pymodule2.py라는 두 개의 Python 스크립트를 만들고, 아래와 같이 동일한 코드를 작성합니다.

#module1
def main():
    print ('module imported', __name__)
    return
if __name__=='__main__':
    main()

이제 importlib 패키지를 사용해 이 모듈들을 동적으로 임포트해 보겠습니다.

>>> import importlib
>>> mod=importlib.import_module('module1')
>>> mod.__name__
'module1'
>>> mod=importlib.import_module('module2')
>>> mod.__name__
'module2'
>>> mod.main()
module imported module2
>>>

실행 결과를 보면 모듈 이름을 문자열로 전달하는 것만으로 런타임에 모듈이 정상적으로 임포트되는 것을 확인할 수 있습니다.

importlib의 주요 하위 모듈

importlib 패키지는 다음과 같은 하위 모듈들로 구성되어 있습니다.

importlib.abc

임포트 과정에서 사용되는 모든 핵심 추상 기반 클래스(ABC)를 담고 있는 모듈입니다. 핵심 ABC를 더 쉽게 구현할 수 있도록 돕는 일부 서브클래스도 함께 제공됩니다.

importlib.resources

Python의 임포트 시스템을 활용하여 패키지 내부에 포함된 리소스(데이터 파일 등)에 접근할 수 있게 해주는 모듈입니다.

importlib.machinery

임포트 시스템이 모듈을 찾고 로드하는 데 필요한 다양한 객체들을 포함하고 있는 모듈입니다.

importlib.util

임포터를 위한 유틸리티 코드가 들어 있는 모듈로, 임포터를 구성하는 데 도움이 되는 여러 객체와 중요한 함수들이 정의되어 있습니다.

importlib.util의 핵심 함수

find_spec()

지정된 패키지 이름을 기준으로 모듈의 명세(specification)를 찾는 함수입니다. 이름이 서브모듈(점을 포함하는 이름)인 경우 부모 모듈이 자동으로 임포트됩니다. namepackage 매개변수의 동작 방식은 import_module()과 동일합니다.

module_from_spec(spec)

specspec.loader.create_module을 기반으로 새로운 모듈 객체를 생성하는 함수입니다.

종합 예제: 모듈 존재 확인 후 동적 임포트

import importlib.util
def check_module(mod):
    spec = importlib.util.find_spec(mod)
    if spec is None:
        print('Module: {} not found'.format(mod))
        return None
    else:
        print('Module: {} can be imported!'.format(mod))
        return spec

def import_module(spec):
    mod = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(mod)
    return mod

if __name__ == '__main__':
    spec = check_module('notamodule')
    spec = check_module('module1')
    if spec:
        mod = import_module(spec)
        mod.main()

실행 결과

Module: notamodule not found
Module: module1 can be imported!
module imported module1

위 예제처럼 find_spec()으로 모듈의 존재 여부를 먼저 검사한 후, module_from_spec()exec_module()을 조합하면 안전하게 모듈을 동적으로 로드할 수 있습니다. 이러한 패턴은 플러그인 시스템이나 런타임에 모듈을 유연하게 불러와야 하는 상황에서 매우 유용하게 활용됩니다.