Problem
파이썬의 패키지(package)는 모듈(ex. “test.py”, “date_utils.py”)이나 하위 디렉토리 (ex. utils/)를 계층 구조로 관리할 수 있게 해준다. 즉, 디렉토리에 접근하는 것과 유사하게 “.”(dot)으로 패키지 내 모듈과 하위 디렉토리에 접근할 수 있다.
└── test
├── __init__.py
├── test.py
└── utils
└── date_utils.py
예를 들어 test.py
모듈에서 utils/date_utils.py
에 있는 함수를 사용하고 싶다고 하자.
# test/test.py
_script_path = os.path.abspath(os.path.dirname(__file__)) # test.py의 절대 경로
_util_path = os.path.abspath(os.path.join(_script_path, 'utils')) # utils 디렉토리의 절대경로
sys.path.insert(0, _util_path) # utils 절대경로를 환경변수에 추가
from date_utils import * # date_util 모듈 import
이렇게 파이썬 환경 변수 (sys.path)에 모듈의 절대 경로를 추가하면 from (module_name) import (function)
코드로 모듈을 불러올 수 있다.
하지만 아래와 같은 상황에서는 환경 변수가 꼬이는 문제가 발생한다. 이번엔 test.py
모듈에서 utils/date_utils.py
에 있는 함수 뿐만 아니라 util_old/date_uils.py
에 있는 함수도 사용하고 싶다고 하자. 이렇게 서로 다른 두 디렉토리에 이름이 동일한 모듈이 존재할 때 문제가 생긴다.
└── test
├── __init__.py
├── test.py
├── util_old
│ └── date_utils.py
└── utils
└── date_utils.py
# test/test.py
_script_path = os.path.abspath(os.path.dirname(__file__)) # test.py의 절대 경로
_util_path = os.path.abspath(os.path.join(_script_path, 'utils')) # utils 디렉토리의 절대경로
_util_old_path = os.path.abspath(os.path.join(_script_path, 'util_old')) # util_old 디렉토리의 절대경로
sys.path.insert(0, _util_path) # utils 절대경로를 환경변수에 추가
from date_utils import * # date_util 모듈 import
sys.path.insert(0, _util_old_path) # util_old 절대경로를 환경변수에 추가
from date_utils import * # date_util 모듈 import
sys.path
에는 utils
의 절대 경로와 util_old
의 절대 경로가 모두 추가되었다. 따라서 date_utils
모듈의 위치는 utils
의 절대 경로가 될 수도 있고, util_old
의 절대 경로가 될 수도 있다.
따라서 파이썬 환경 변수(sys.path
)에 모듈이 위치한 절대 경로를 추가하는 방법은 모듈 이름이 겹치는 경우에 안정성을 보장할 수 없다는 문제가 발생한다.
Solution
파이썬 패키지는 “.”(dot)을 이요해 계층 구조를 관리한다고 했다. 따라서 패키지 내 모듈에 접근할 때 “.”(dot)을 이용해 모듈의 상대 경로(메인 모듈에 따라 다름) 접근할 수 있다.
메인 모듈
- 인터프리터에서 실행하는 모듈
__name__
이__main__
으로 설정됨
메인 모듈은 인터프리터에서 직접 실행하는 파일로 현재 예시에서는 test.py
모듈이 메인 모듈이 된다.
# test/test.py
print(__name__)
위의 test.py을 실행하면 아래와 같은 결과물이 나온다.
메인 모듈의 __name__
변수는 항상 __main__
값을 갖는다.
python3 test/test.py
>>> __main__
절대 경로
메인 모듈에서는 절대 경로로 모듈을 import 해야 한다.
# test/test.py
from utils.date_utils import * # utils/date_utils.py 모듈의 모든 함수를 가져옴
만약 아래와 같이 메인 모듈에서 상대 경로를 사용하면 importerror가 발생한다.
# test/test.py
from .utils.date_utils import * # utils/date_utils.py 모듈의 모든 함수를 가져옴
“parent package”가 정의되지 않아 상대경로를 사용할 수 없다는 에러이다.
Reference
Python-ImportError-attempted-relative-import-with-no-known-parent-package sys-path-pythonpath jumptopython