오늘도 개발

파이썬 모듈과 패키지(Module & Package) 본문

웹 프로그래밍/Python3

파이썬 모듈과 패키지(Module & Package)

Sueeeeeee 2022. 6. 23. 18:56

1. 모듈과 패키지의 개념

모듈과 패키지는 다른 파이썬 프로그램에서 불러와 사용하기 위해 만든다.
- 모듈 : 함수, 변수, 클래스 등을 모아놓은 파일.
- 패키지 : 모듈의 모음. 디렉터리 구조로 되어있다.

2. 파이썬이 모듈과 패키지를 찾는 곳

abc.py라는 파일을 모듈로 사용한다고 가정해보자.
test.py에서 다음과 같이 코드를 작성하면 abc 모듈을 사용할 수 있다.

import abc

파이썬은 'import abc'라는 줄을 보면 abc.py 파일이 어디있는지 찾기 시작한다.
먼저 sys.modules에서, 없으면 built-in modules에서, 그래도 없으면 sys.path에서 찾고
그래도 없으면 ModuleNotFoundError를 낸다. (패키지의 경우도 마찬가지다.)

1) sys.modules

sys라는 모듈에 저장된 modules라는 이름의 딕셔너리이다.
modules 딕셔너리 안에는 파이썬 실행 이후로 import된 모든 모듈과 패키지의 경로가 저장되어있다.

2) built-in modules

파이썬에서 제공하는 공식 라이브러리이다. (예: sys 모듈)

3) sys.path

sys라는 모듈에 저장된 path라는 이름의 리스트이다.
path 리스트 안에는 모듈을 import할 때 찾아보아야 할 경로들이 저장되어 있다.

파이썬 파일(test.py)이 위치한 디렉터리, 환경 변수 PYTHONPATH의 값 등이 디폴트로 저장되어 있다.
pip로 다운받은 파일은 파이썬에서 import로 사용하는 순간 sys.path에 저장된다.

Q1) sys.modulessys.path의 차이점
sys.path에는 import 시 찾아보아야 하는 모든 경로가 저장되어 있다.
반면 sys.modules는 import를 이미 실행한 모듈/패키지의 경로만 저장하는 캐시이기 때문에
한 번도 import된 적 없는 모듈/패키지의 경로는 sys.modules에서 찾을 수 없다.

Q2) 파이썬이 sys 모듈 위치를 찾는 방법
sys는 파이썬이 제공하는 공식 모듈이므로 built-in modules에서 찾는다.

3. 절대 경로와 상대 경로(Absolute Path & Relative Path)

직접 개발한 패키지를 import하는 경우 해당 패키지의 import 경로를 선언해주어야 한다.
경로는 절대 경로 또는 상대 경로로 작성할 수 있다.

아래와 같은 구조로 된 프로젝트를 가정해보자.

절대 경로 (Absolute Path)

프로젝트의 최상단 디렉터리를 기준으로 경로를 정의하는 방식이다.
위 프로젝트의 module4 파일에서 module5를 절대경로를 이용하여 import하는 방법은 다음과 같다.

from package2.subpackage1.module5 import func

import하는 파일의 위치에 상관없이 절대 경로는 항상 동일하다.

상대 경로(Relative Path)

import하는 파일 위치를 기준으로 경로를 정의하는 방식이다.
위 프로젝트의 module4 파일에서 module5를 상대 경로를 이용하여 import하는 방법은 다음과 같다.

from .subpackage1.module5 import func

module2 파일에서 module5를 상대 경로를 이용하여 import하면 이렇게 된다.

from ..package2.subpackage1.module5 import func

같은 파일이라도 import하는 파일의 위치에 상대 경로는 다르게 정의된다.
그렇기 때문에 상대 경로를 사용할 때는 오류가 발생할 가능성이 더 크다.

반면 상대 경로는 간결하게 경로를 작성할 수 있다는 장점이 있어서
주로 로컬 패키지 안에서 다른 로컬 패키지를 import할 때 사용한다.

4. Main 모듈

파이썬에는 C언어나 자바의 main 함수가 없다.
대신 main 모듈이 존재한다.
파이썬이 처음으로 실행한 파일을 main 모듈이라고 한다.

파이썬 프로그램을 실행할 때 터미널에 입력하는 파일이 메인 모듈이다.
터미널에 python3 test.py를 입력한다면 test.py가 메인 모듈이다.

아래 예시를 통해 더 자세히 살펴보자.
나는 test.py에 func 모듈을 임포트해서 사용할 것이다.
그러기 위해 다음과 같이 파일을 만들고,

<func.py>

def add(a, b):
    return a + b

print(__name__)
# __name__ 변수는 파이썬이 내부적으로 사용하는 특별한 변수 이름
# 현재 모듈 이름이 저장되어 있다

<test.py>

from func import add

print(add(1, 2))
print(__name__)


터미널에 아래와 같이 입력할 것이다.

python3 test.py


그럼 파이썬 인터프리터는 다음과 같이 파일을 실행한다.
1) test.py 실행
2) func.py 실행
2) test.py 마저 실행

파이썬이 처음으로 실행한 파일은 test.py이다.
그러므로 여기서는 test.py가 메인 모듈이다.

파이썬이 프로그램을 실행할 때 가장 먼저 처리하는 코드는 import이다.
그래서 test.py를 실행하면 즉시 func.py가 실행된다.

그렇기 때문에 python3 test.py를 입력하면 다음과 같은 결과가 뜬다.

# test.py 실행 시작
# func.py 모듈의 실행 결과
func
# test.py를 마저 실행한 결과
3
__main__


만약 func.py를 메인 모듈로 사용할 때만 print()코드가 실행되게 하려면 다음과 같이 코드를 바꾸면 된다.
<func.py>

def add(a, b):
    return a + b

if "__name__" == "__main__"
	print(1)

이제 터미널에서 python3 func.py를 실행하면 다음과 같이 출력되며,

1

python3 test.py를 실행하면 다음과 같이 출력된다.

3
__main__

5. Calculator 패키지 만들기

<패키지 구조>

<main.py>

# absoulte path
from calculator.add_and_multiply import add_and_multiply 

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

메인모듈에서는 항상 절대경로로 모듈을 import 해야 한다.
만약 다음과 같이 상대경로로 import를 하면

# relative path
from .calculator.add_and_multiply import add_and_multiply 

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

이런 오류가 난다.

Traceback (most recent call last):
  File "main.py", line 5, in <module>
    from .calculator.add_and_multiply import add_and_multiply
ImportError: attempted relative import with no known parent package

왜 상위 패키지가 없다고 할까?
상대 경로를 작성하면 파이썬은 현재 모듈의 위치를 기준으로 import할 모듈의 경로를 찾는다.
이 때 현재 모듈이 메인 모듈인 경우 파이썬은 현재 위치를최상위 디렉터리로 인식한다.

파이썬은 어떻게 메인 모듈을 최상위 디렉터리라고 인식할까?
파이썬 파일을 실행하면 파이썬이 __name__ 변수에 현재 파일의 이름을 저장한다.
현재 파일이 메인 모듈인 경우 항상 __main__으로 저장되며
현재 파일이 메인 모듈에 import된 파일의 경우 원래 파일명으로 저장된다.

(예를 들어 test.py 파일에서 func.py를 임포트하여 사용하면
test.py의 __name__은 __main__으로 저장되고
func.py의 __name__은 func로 저장된다.)

상대 경로를 해석할 때 파이썬은 __name__에 저장된 이름을 보고 현재 위치를 파악하는데,
이름이 __main__인 경우에는 자동으로 현재 위치를 최상위 디렉터리로 해석한다.

<add_and_multiply.py>

from .multiplication import multiply

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


메인모듈이 아닌 일반모듈이 다른 모듈을 import할 때는 절대경로, 상대경로를 둘 다 사용할 수 있다.

main.py에서 add_and_multiply를 import하면 add_and_multiply가 실행된다.
파이썬은 add_and_multiply의 __name__을 add_and_multiply로 설정하고 add_and_multiply 모듈이 import 할 파일을 실행한다.

상대 경로로 파일 경로가 작성된 경우 파이썬은 __name__의 add_and_multiply를 보고 add_and_multiply.py 파일을 현재 위치로 인식한다.

절대 경로로 파일 경로가 작성된 경우 파이썬은 메인 모듈과 같은 방식으로 import할 파일을 찾는다.


<multiplication.py>

def multiply(a,b):
    return(a*b)

<__init__.py>
Q3) __init__.py의 역할
파이썬은 __init__.py가 있는 디렉터리를 패키지로 인식한다.
어떤 디렉터리를 패키지로 만들고 싶으면 __init__.py 파일을 생성해서 넣으면 된다.
__init__.py 파일 내부에는 아무것도 작성할 필요가 없다.




- add_and_multiply.py에서 multiply를 절대경로, 상대경로로 임포트 해보고 메인 모듈과 차이점 생각하고 결과 출력

참고
위코드 1주차 강의
실용 파이썬 프로그래밍 : 프로그래밍 유경험자를 위한 강좌 - 3.5 메인 모듈
파이썬에서 직접 만든 패키지를 불러오자. (feat. 절대 경로와 상대 경로)
docs.python.org - 6.4.2 Intra-package References