Python | 모듈과 패키지 찾는 방법

happy tiger·2022년 6월 26일
1

Python

목록 보기
4/4
post-thumbnail

🔍 Import시 찾는 순서

import tigermod

tigermod라는 module/package가 있다고 가정해보자.
그리고 tigermodmodule/package를 사용하려면 import를 해야 한다.

import를 하기 위해선 module/package를 찾아야 할텐데 어떤 순서로 찾을까?
1. sys.modules
2. built-in modules
3. sys.path

sys.modules

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

아래는 sys.modules이다.

from mymod import funcmymod는 sys.modules에 있을까?

from mymod import func을 할 때 컴퓨터는 mymod전체를 읽어들인다. 단지, sys.modules에 포함되지 않을 뿐이다.
고로, from mymod import func한 후, import mymod를 해도, 추가적인 메모리 다운은 없다. 왜냐하면, from mymod import func했을 때 mymod를 모두 받았기 때문이다.
다르게 말하면 from mymod import func를 해도, import mymod보다 메모리가 절약되지 않는다.

built-in modules

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

sys.path

마지막으로 보는 장소가 바로 sys.path이다.
sys.path는 현재 자신의 컴퓨터가 가지고 있는 모든 모듈과 패키지들의 위치를 알려준다.
sys.path는 기본적으로 list이며 string 요소들을 가지고 있는 list 이다.
sys.path에서 파이썬은 list의 각 경로를 하나 하나 확인하면서 해당 경로에 import 하고자 하는 package가 위치해 있는지 확인한다.

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

import sys
print(sys.path)
print(sys.builtin_modules_name)
print(sys.modules)

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

sys.path 에서도 못찾으면 ModuleNotFoundError 에러를 리턴한다.

🔍 Absolute Path & Relative Path

파이썬의 built-in 모듈과 pip 을 통해 설치한 외부 모듈 및 package는 일반적으로 import 하는데 큰 문제가 되지 않는다.
Built-in 모듈은 당연히 잘 찾아지고, pip 으로 설치한 외무 모듈도 자동으로 site-packages 라는 디렉토리에 설치가 되는데, 이 site-packages 는 sys.path에 이미 포함되어 있기때문에 찾는데 문제가 없다.
문제는 직접 개발한 local package 이다. 직접 개발한 local package를 import 할때는 해당 package의 위치에 맞게 import 경로를 잘 선언해야 한다.
Local package를 import 하는 경로에는 absolute path 와 relative path 가 있다.

Absolute path

Absolute path는 이름 그대로 절대 경로이다. 왜 절대 경로인가 하니, import를 하는 파일이나 경로에 상관없이 항상 경로가 동일하기 때문이다.
다음과 같은 프로젝트를 예를 들어 보자:

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

my_app 이라는 프로젝트 이며 package1과 package2 라는 2개의 package를 가지고 있다.
그리고 package2는 subpackage2 라는 중첩 package를 가지고 있다.
Absolute path를 사용해 package1 과 package2를 import 하면 다음과 같다.

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

경로들의 시작점이 전부 my_app 프로젝트의 가장 최상위 디렉토리에서 시작하는것을 볼 수 있다.
예를 들어, subpackage1의 module5 모듈의 function2 함수를 import 하기 위해서는 다음 경로를 거치게 된다.
my_app => package2 => subpackage1 => module5.py

이걸 리눅스의 directory 경로 형식으로 바꾸면 다음처럼 표현 할 수 있다.
my_app/package2/subpackage1/module5.py
윈도우스 형식이라면 다음과 같다.
my_app\package2\subpackage1\module5.py
파이썬에서는 slash / 나 back slash \ 대신에 dot . 을 사용해서 경로를 표현한다.
my_app.package2.subpackage1.module5.py
이미 my_app 프로젝트 안에 있으므로 my_app 은 생략된다. 그러므로 다음처럼 경로를 표현하게 되는 것이다.
package2.subpackage1.module5.py
이걸 from import 키워드를 사용해 import 하게 되면 다음 처럼 되는 것이다.
from package2.subpackage1.module5 import function2
my_app 프로젝트 내에서는 어느 파일, 어느 위치에서 import 하던지 경로가 항상 위와 같이 동일하게 되므로 absolute path 라고 한다.

절대 경로의 시작점은 어디???

참고로 current directory 라고 하는 현재의 프로젝트 디렉토리는 default로 sys.path 에 포함되게 된다.
그러므로 absolute path는 current directory 로 부터 경로를 시작하게 되는것이다.
일반적으로 local package를 import 할때는 absolute path를 사용하면 된다.
다만 absolute path를 사용하게 되면 한가지 단점이 있는데 바로 경로가 길어질 수 있다는 점이다.
그래서 이러한 단점을 보완하기 위해서 relative path를 사용할 수 있다.

Relative path

Relative path는 absolute path와 다르게 프로젝트의 최상단 디렉토리를 기준으로 경로를 잡는게 아니라 import 하는 위치를 기준으로 경로를 정의한다. 그래서 일반적으로 relative path는 local package 안에서 다른 local package를 import 할때 사용된다.
예를 들어, package2의 module3에서 package2의 class1과 package2의 하위 package인 subpackage1의 module5의 function2 함수를 import 하려고 하면 다음 처럼 할 수 있다.

# package2/module3.py

from . import class1
from .subpackage1.module5 import function2

여기서 dot .은 import 가 선언되는 파일의 현재 위치를 이야기한다.
현재위치는 package2/module3.py 이므로 현재 위치에서부터 원하는 모듈의 경로만 선언해주면 되는 것이다.

또한 dot 2개를 사용할 수도 있다. dot 2개 .. 는 현재위치에서 상위 디렉토리로 가는 경로이다.

# subpackage1/module5.py
from ..module4 import class4

Relative path는 선언해야 하는 경로의 길이를 줄여준다는 장점은 있지만 헷갈리기 쉽고 파일 위치가 변경되면 경로 위치도 변경되어야 하는 단점이 있다.
그러므로 웬만한 경우 absolute path를 사용하는게 권장된다.

❕ 이해했다면 풀어보자

1. sys.modules 와 sys.path의 차이점을 서술하라.

답변

  • sys.modulesimport가 된 모듈과 패키지들이 있고, sys.path는 해당 컴퓨터에 내장은 되어있지만 import가 되지않은 모듈과 패키지들이 있다.
  • sys.modules는 dictionary 형태이고, sys.path는 list형태이다.
  • sys.modules는 파이썬이 모듈 혹은 패키지를 찾을 때 가장 먼저 찾는 곳이고, sys.path는 가장 마지막으로 찾는 곳이다.

2. sys 도 import 해야하는 모듈이다. 파이썬은 sys 모듈의 위치를 어떻게 찾을 수 있을까?

답변

파이썬은 해당 모듈이나 패키지를 찾을 때까지 3곳을 들린다.
첫 번째로, import가 된 모듈과 패키지들이 있는 sys.modules에 들려 찾는다.
sys모듈이 import된 적이 있다면, sys.modules에서 발견할 수 있으므로 탐색은 종료된다.
하지만 만약 sys모듈이 import된 적이 없다면, sys.modules에서 찾을 수 없으므로 파이썬은 파이썬 내장 라이브러리인 built-in modules를 확인한다.
sys모듈은 built-in modules이므로 3번째 단계인 sys.path를 탐색하지 않고, sys모듈을 발견할 수 있다.

3. Absolute path와 relative path의 차이점을 서술하라.

  • 절대 경로의 시작점은 해당 프로젝트의 가장 최상위 디렉토리이고, 상대 경로의 시작점은 import하는 위치이다.
  • 절대 경로import하는 파일이나 경로에 상관없이 항상 경로가 동일하므로, 절대경로 라고 불린다. 상대 경로import하는 파일이나 경로에 따라 경로가 달라지므로, 상대 경로 라고 불린다.
  • 절대 경로는 경로가 길어질 수 있다는 단점이 있고, 상대 경로는 경로의 길이가 짧지만 헷갈리기 쉽고 경로 위치도 변경될 수 있다는 단점이 존재한다.

답변

4. main.py에서 상대경로로 add_and_mutiply를 임포트 했을 때 발생하는 에러를 확인하고, main module 에서는 패키지의 모듈을 어떻게 임포트 해야하는지 설명하라.


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)

답변

__name__은 모듈의 이름이 저장되는 변수이며 import로 모듈을 가져왔을 때 모듈의 이름이 들어간다. 하지만 파이썬 인터프리터로 스크립트 파일을 직접 실행했을 때는 모듈의 이름이 아니라 __main__이 들어간다.

파일을 실행하면 해당 파일은 메인파일 즉, __name__=='__main__'파일이 된다.
여기선 main.py파일을 실행했으므로 main.py파일이 __main__파일이 되는 것이다.
__main__파일이 되면, 해당 파일은 최상위 파일이 된다.
즉, 자신이 최상위가 되기 때문에 자신의 위레벨이나 자신과 같은 레벨로 이동할 수 없어진다.
하지만, 지금 코드에서는 from .calculator.add_and_multiply 로 상대 경로를 사용하고 있다. 그래서 ImportError가 발생하는 것이다.
고로, __main__파일에서 add_and_multiply.py에 접근하기 위해선 절대 경로를 이용하면 된다.

# 수정 전 main.py 파일 일부 (상대 경로)
from .calculator.add_and_multiply import add_and_multiply
# 수정 후 (절대 경로)
from calculator.add_and_multiply import add_and_multiply

5. add_and_multiply.py에서 multiply함수를 절대경로와 상대경로도 각각 임포트 해보고 main 모듈과 차이점을 생각해보고 결과를 출력하라.

답변

상대경로인 from .multiplication import multiply
절대경로인from calculator.multiplication import multiply로 접속해도
main.py 파일은 모두 성공적으로 작동한다.
실행하는 파일은 main.py파일이기에, __main__파일인 main.py파일이 아닌 다른 파일들은 상대경로와 절대경로로 모두 사용 가능하다.

6. init.py 파일의 역할에 대해서도 정리하라.

답변

__init__.py 파일은 해당 디렉토리가 패키지, 패키지의 일부임을 알려주는 역할을 한다. 만약 패키지에 포함된 디렌터리에 __init__.py파일이 없다면 패키지로 인식되지 않는다.
_python 3.3버전부턴 __init__.py파일이 없어도 패키지로 인식하다. 하지만 하위버전 호환을 위해 __init__.py파일을 생성하는 것이 안전한 방법이다.

📑 참고자료

점프 투 파이썬
파이썬 코딩도장
Wecode - Replit: Python 57

profile
호기심·끈기·성장·발전·행복·협력 ٩(๑•̀ㅂ•́)و

0개의 댓글