How Import statement finds modules and packages

secret0831·2022년 6월 27일
0

1. sys.modules vs. sys.path

  • sys.modules
    전체적으로 sys 모듈에 대해 정리해 놓은 페이지가 있다. 그걸 번역 해 보면

  • sys.modules의 값은 이미 로드된 모듈이 그 이름과 쌍을 이루는 딕셔너리입니다.

  • 로드된 모듈을 다시 불러오는 일이 없도록 관리하기 위한 딕셔너리라고 생각한다.

  • sys.path
    sys.path는 파이썬 라이브러리들이 설치되어 있는 디렉터리들을 보여준다. 만약 파이썬 모듈이 위의 디렉터리에 들어 있다면 모듈이 저장된 디렉터리로 이동할 필요없이 바로 불러서 사용할 수가 있다. 아래는 sys.path를 인터프리터에서 실행했을 때 결과이다.

['',
'/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python37.zip', 
'/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7', 
'/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload', 
'/Users/choonghee/Library/Python/3.7/lib/python/site-packages', 
'/usr/local/lib/python3.7/site-packages']
리스트 형태이다. 그렇다! 필요한 모듈이 있는 경로를 append() 함수로 추가할 수 있다!!

결론
파이썬이 모듈을 확인할 때,
sys.modules는 파이썬이 제일 먼저 확인하는 장소이며 Dictionary이고
sys.path는 파이썬이 제일 마지막에 확인하는 장소이고 List이다.

2. 파이썬은 어떻게 sys 모듈의 위치를 찾을까?

PyMOTW-3 페이지를 보고 영감을 얻었다. 참고로 PyMOTW란 Python Module of the Week이다.

파이썬 인터프리터는 일부 내장된 C 모듈과 함께 컴파일 된다. 그래서 해당 모듈들은 따로 분리된 라이브러리일 필요가 없다. 이러한 내장 모듈들은 엄밀히 말하면 import 되는게 아니기 때문에 sys.modules에 나타나지 않는다. 내장 모듈들의 이름을 보고싶다면 다음과 같은 코드를 사용한다.


import sys

for name in sys.builtin_module_names:
    print(name)

그럼 다음과 같은 결과가 나타난다.

_abc
_ast
_codecs
_collections
_functools
..
중간생략
..
posix
pwd
sys
time
xxsubtype
zipimport

결론은 인터프리터에 이미 내장되어 있으므로, 인터프리터를 실행하면 그냥 로드된 것이다!

3. absolute path vs. relative path

absolute imports
Absolute imports는 프로젝트의 루트 폴더에서 부터 full-경로를 사용하여 import될 리소스를 명시하는 것이다.

  • 예를 들어볼까요?
└── project
    ├── package1
    │   ├── module1.py
    │   └── module2.py
    └── package2
        ├── __init__.py
        ├── module3.py
        ├── module4.py
        └── subpackage1
            └── module5.py
package1/module2.py의 function1이 필요합니다.
package2/__init__.py에 class1이 있다고 가정하고 class1이 필요합니다.
package2/subpackage1/module5.py의 function2도 필요합니다.

코드로 표현해보자면

from package1.module2 import function1
from package2 import class1
from package2.subpackage1.module5 import function2

여기서 눈여겨 봐야할 점은 각 패키지 또는 파일의 상세한 경로를 탑-레벨 패키지 폴더에서 부터 지정해줘야 한다는 점이다. 파일 경로와 비슷한 점이 있지만 슬래쉬(/)대신 점(.)을 사용해야한다.

Absolute Imports의 장단점

장점! 일단 명쾌하게 import된 리소스의 위치를 파악할 수 있다. 사실, PEP8은 absolute imports를 명시적으로 추천한다!

하지만.. 디렉토리의 구조가 복잡해지면 살벌해진다 🔪.

from package1.subpackage2.subpackage3.subpackage4.module5 import function6
이런 경우에는 relavtive imports를 사용하자!

relative imports
relative imports는 현재 위치에 상대적으로 import될 리소스를 명시하는 것이다. implicit한 방법과 explicit한 방법이 있지만 파이썬 3에서 implicit relative imports는 deprecated되었으므로 다루지 않는다.

예시를 봅시다
relative imports 구문은 import될 모듈, 패키지, 객체의 위치 뿐만 아니라 현재의 위치에 따라 달라진다. 위에서 봤던 디렉토리 구조를 다시 사용해 예시를 들어본다.


└── project
    ├── package1
    │   ├── module1.py
    │   └── module2.py
    └── package2
        ├── __init__.py
        ├── module3.py
        ├── module4.py
        └── subpackage1
            └── module5.py

시작하기전 알아야하는 것은 하나의 점(.)은 현재 위치와 같은 디렉토리의 모듈이나 패키지를 가리킨다. 두 개의 점(..)은 현재 위치의 부모 디렉토리를 가리킨다는 것이다. 세 개의 점을 이용해서 부모의 부모 디렉토리도 가리킬 수 있고 계속 그렇게 점을 늘려가도 되지만, 개인적으로 본 기억이 없다.

package1/module1.py

from .module2 import function1
module1에서 module2의 function1을 import 한 것이다. 시작하기 전에 말한 점-규칙을 잘 생각해보면 이해할 수 있을 것이다.

relative imports의 장단점

relative imports는 당연히 간결한 구문이 장점이다. 위에서 봤던 복잡한 코드는 다음과 같이 간단해질 수 있다.

from ..subpackage4.module5 import function6
하지만 absolute imports에 비해서 가독성이 떨어지고, 다른 사람과 협업시 디렉터리 구조가 바뀔 가능성이 높으므로 오류가 발생할 가능성도 높아진다.

4. 패키지 모듈을 main 모듈에서 어떻게 import 할까?

오류 확인!
일단 다음과 같은 구조의 프로젝트가 있다.

├── calculator
│   ├── __init__.py
│   ├── add_and_multiply.py
│   └── multiplication.py
└── main.py

그리고 main.py에는 다음과 같은 코드가 있다. 나머지 파일들이 어떤 코드를 가졌는지 생각하지 말고 "산은 산이요 물은 물이로다"하는 마음으로 보면된다.

relative path

from .calculator.add_and_multiply import add_and_multiply

if name == 'main':
print(add_and_multiply(1,2))
실행해보면 다음과 같은 오류가 발생한다.

ImportError: attempted relative import with no known parent package

해석해 보면 부모 패키지도 없는데 relative import를 시도했다는 뜻이다. 그럼 main 모듈에서 어떻게 import를 해야할까??

그래서 어떻게 하죠??
파이썬 문서를 보면 메인 모듈에서 어떻게 import 해야하는지 적혀있다.

Note that relative imports are based on the name of the current module. Since the name of the main module is always "__main__", modules intended for use as the main module of a Python application must always use absolute imports.

음...해석을 해보도록 하겠습니다

relative imports는 현재 모듈의 이름을 기반으로 한다. 메인 모듈의 이름은 항상 "main"이기 때문에, 파이썬 어플리케이션의 메인 모듈로 사용하려는 모듈은 항상 absolute imports를 사용해야한다.

뭔 말..?

"메인 모듈을 보통 프로젝트의 루트에 있기 때문에 찾을 부모 디렉터리가 없어서 relative imports를 하면 에러가 난다"고 멘토님이 말씀해주셔서 이해가 되었다

5. init.py의 역할

init.py... 그 때는 "이게 뭐고? 암것도 없는데 왜 만드는거지" 안그래도 궁금했는데 이제야 알게 됬다 ㅋㅋ. 그것의 역할을 몇가지 정리해본다.

이 파일이 존재하는 디렉터리가 패키지임을 명시한다.
공통으로 적용가능한 기능이나 모듈을 포함하여 한 번의 선언으로 해당 경로에 위치한 모든 파일들이 그것을 이용하게 한다.
필요에 따라 import 경로의 길이를 짧게할 수 있다.
all 변수를 통해 import 할 수 있는 변수/함수/클래스를 제한 할 수 있다.

profile
잇츠미

0개의 댓글