TIL no.18 - Python - import 는 어떻게 모듈과 패키지를 찾는가?

codeamor·2020년 7월 22일
0

Python

목록 보기
4/4

1. sys.modules 와 sys.path의 차이

sys.modules

  • 파이썬이 모듈이나 package를 찾기위해 가장 먼저 확인하는 곳이다.
  • sys.modules는 단순한 dictionary 이다.
  • 이미 import된 모듈과 package들을 저장하고 있다.
  • 즉, 한번 import된 모듈과 package들은 파이썬이 또 다시 찾지 않아도 되도록 하는 기능을 가지고 있다.
    • 그러므로 새로 import 하는 모듈은 sys.modules 에서 찾을 수 없다.

sys.path

  • sys.path는 기본적으로 list이며 string 요소들을 가지고 있는 list 이다.
  • 각 string 요소들은 다음 처럼 경로를 나타낸다
['',
 '/Users/song-eun-u/anaconda3/bin',
 '/Users/song-eun-u/anaconda3/lib/python36.zip',
 '/Users/song-eun-u/anaconda3/lib/python3.6',
 '/Users/song-eun-u/anaconda3/lib/python3.6/lib-dynload',
 '/Users/song-eun-u/anaconda3/lib/python3.6/site-packages',
 '/Users/song-eun-u/anaconda3/lib/python3.6/site-packages/aeosa',
 '/Users/song-eun-u/anaconda3/lib/python3.6/site-packages/IPython/extensions',
 '/Users/song-eun-u/.ipython']

📌️ 따라서 파이썬은 list의 각 경로를 하나 하나 확인하면서 해당 경로에 import 하고자 하는 package가 위치해 있는지 확인한다.

📌️ sys 는 파이썬에 포함되어 있는 모듈이다. 그러므로 다음 처럼 sys 모듈을 import 해서 sys.modules와 sys.path 를 출력할수도 있고 수정 할 수도 있다.

import sys

print(sys.path)
print(sys.modules)

📌️ 요약하면, 파이썬은 import 하고자 하는 모듈과 package를 찾을 때 먼저 sys.modules를 보고, 없으면 파이썬 built-in 모듈들을 확인하고 마지막으로 sys.path에 지정되어 있는 경로들을 확인해서 찾는다.
sys.path 에서도 못찾으면 ModuleNotFoundError 에러를 리턴한다.

* built-in modules

  • 파이썬에서 제공하는 파이썬 공식 라이브러리들이다.
  • Built-in 모듈들은 이미 파이썬에 포함되어 나오므로 파이썬이 쉽게 찾을 수 있다.

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

python이 module 과 package를 찾는 순서

  1. sys.modules

  2. built-in modules

  3. sys.path

  • sys.modules는 built-in 모듈이기때문에 built-in에서 찾는다.

  • 밑에 sys를 import하고 sys.modules를 프린트하면 bulit-in이라고 나온다.

3. absolute path와 relative path의 차이

  • absolute path는 프로젝트 내에서는 어느 파일, 어느 위치에서 import 하던지 경로가 항상 위와 같이 동일하다.

  • relative path는 프로젝트의 최상단 디렉토리를 기준으로 경로를 잡는게 아니라 import 하는 위치를 기준으로 경로를 정의한다.

현재 경로: package2의 module4
subpackage1의 module4의 func()함수를 import하려고하면

  • 절대 경로
    from package2.subpackage1.module5 import func()

  • 상대 경로
    from .subpackage1.module5 import func()


4. Calculator 패키지 만들기

  • main.py
# absoulte path
#from calculator.add_and_multiply import add_and_multiply 

# relative path
from .calculator.add_and_multiply import add_and_multiply

if __name__ == '__main__':
    print(add_and_multiply(1,2))
  • add_and_multiply.py
from .multiplication import multiply
# from calculator.multiplication import multiply
def add_and_multiply(a,b):
    return multiply(a,b) + (a+b)
  • multiplication.py
def multiply(a,b):
    return(a*b)

5. main.py 를 상대 경로로 import 한 경우

# absoulte path
# from calculator.add_and_multiply import add_and_multiply 

# relative path
from .calculator.add_and_multiply import add_and_multiply

if __name__ == '__main__':
    print(add_and_multiply(1,2))
    
>>> No module named '__main__.calculator'; '__main__' is not a package

메인모듈의 name 값은 relative path 일때는 파일명에 종속되고
absolute path일 때에 main 이 된다.

절대경로로 임포트해야 실행할 수 있다.

# absoulte path
from calculator.add_and_multiply import add_and_multiply 

if __name__ == '__main__':
    print(add_and_multiply(1,2))
    
>>> 5

6. add_and_multiply.py에서 multiply함수를 절대경로와 상대경로로의 임포트

  • 상대경로로 임포트했을 경우
from .multiplication import multiply

def add_and_multiply(a,b):
	return multiply(a,b) + (a+b)
    

error => Attempted relative import in non-package

상대경로로 임포트 하면 main.py의 name이 파일명으로 바뀌어 에러가 발생한다.


  • 절대경로로 임포트했을 경우
from multiplication import multiply

def add_and_multiply(a,b):
	return multiply(a,b) + (a+b)

절대경로로 임포트하면 main 모듈로 바뀌면서 main.py에 영향을 주지 않게 되어 실행이 된다.


7. init.py

init의 양옆에 언더바 2개씩이 있다. (velog에서 마크다운과 겹치기 때문에 편의상 언더바 없이 쓰겠다.)


양쪽에 언더바 2개를 가진 이름은 특별 취급을 받는다.
즉, 패키지를 import 할 때 가장 처음으로 실행되는 모듈이다.

  • init.py 파일은 빈 파일이지만 해당 디렉토리가 패키지임을 알려주는 역할을 한다.
    • init.py 파일이 없으면 패키지로 인식하지 않았으나 python 3.3 부터는 없어도 패키지로 인식하게 된다. 그러나 안전상의 이유로 만들어두는 것이 좋다.

또한 main.py 에서 절대 경로로 import 했던 add_and_multiply 모듈을 init.py 를 통하면 경로를 줄일 수 있고 Package에서 import 할 수 있는 변수/함수/클래스 제한하는 기능도 할 수 있다.

0개의 댓글