# [Python] 모듈과 패키지 관리

## 모듈

---

- 함수나 변수 또는 클래스를 모아 놓은 파이썬 파일

- 이미 만들어 놓은 모듈을 사용할 수 있고, 직접 만들어 사용할 수도 있다

### 모듈 만들기

```javascript
# mod1.py
def add(a, b):
    return a + b

def sub(a, b): 
    return a-b
```

- `C:\dot` 디렉토리에 `mod1.py` 파일을 저장

- 파이썬 확장자 `.py`로 만든 파이썬 파일은 모두 모듈

### 모듈 불러오기

```javascript
C:\Users\pahkey>cd C:\doit
C:\doit>dir
C:\doit>python
```

- `C:\dot` 디렉터리로 이동 후 `mod1.py` 모듈을 읽을 수 있다

```javascript
import mod1
print(mod1.add(3, 4))
print(mod1.add(4, 2))
```

- `mod1.py` 모듈을 불러오기 위해 `import mod1`를 입력

```javascript
import 모듈_이름                # 전체 모듈을 import 하는 경우
from 모듈_이름 import 모듈_함수  # 모듈 이름 없이 함수의 이름만 사용하고 싶은 경우
from 모듈_이름 import *         # 모듈의 전체 함수를 import
```

- 모듈을 불러올 때는 `import 모듈_이름`과 같이 .py 확장자를 제거한 이름을 사용

- `from 모듈_이름 import 모듈_함수`의 형태로 모듈의 특정 함수를 모듈 이름 없이 사용 가능

**if ****name**** == “****main****”**

```javascript
# mod1.py
def add(a, b): 
    return a+b

def sub(a, b): 
    return a-b

print(add(1, 4)) # 5
print(sub(4, 2)) # 2
```

```javascript
# mod1.py
def add(a, b): 
    return a+b

def sub(a, b): 
    return a-b

if __name__ == "__main__":
    print(add(1, 4)) # 5
    print(sub(4, 2)) # 2
```

- `if __name__ == “__main__”`를 사용하지 않는 경우 `mod1`을 `import`할 때 5, 2가 출력

- `if __name__ == “__main__”`를 사용하면 `python mod1.py` 처럼 파일이 실행되었을 때는 다음 문장이 실행, 모듈로 `import`할 경우엔 수행되지 않음

**클래스나 변수 포함 모듈**

```javascript
# mod2.py
PI = 3.141592

class Math: 
    def solv(self, r): 
        return PI * (r ** 2) 

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

```javascript
import mod2
print(mod2.PI) # mod2.py파일에 있는 PI 변수의 값을 사용할 수 있다

a = mod2.Math()
print(a.solv(2)) # 12.566368
print(mod2.add(mod2.PI, 4.4)) # 7.541592
```

### 모듈 불러오기

**다른 파일에 모듈 불러오기**

```javascript
# modtest.py
import mod2
result = mod2.add(3, 4)
print(result)
```

- 다른 파이썬 파일에서도 `import mod2`로 `mod2` 모듈을 불러와서 사용할 수 있다

- 이때 `modtest.py`는 `mod2.py` 파일과 동일한 디렉터리 내에 있어야 한다

**다른 디렉터리에 있는 모듈 불러오기**

```javascript
# sys.path.append 사용하기
import sys
sys.path.append("C:/doit/mymod")

# PYTHONPATH 환경 변수 사용하기
set PYTHONPATH=C:\doit\mymod
```

### 모듈의 내부 동작 원리

**Python 모듈**

- Python 코드를 담고 있는 `.py` 파일

    - 클래스, 또는 변수들을 포함

    - Python 프로그램에서 재사용 가능

**설치된 모듈 확인**

```javascript
pip list
```

**패키지 설치 폴더**

- `pip install`  명령어를 사용하면 PyPI로부터 패키지를 다운로드 받아 `site-package` 디렉토리에 설치 (`Python311/Lib/site-packages`, Lib 폴더 아래 있다는 것을 주의)

- `python3 -m site` 명령어로 설치된 폴더 확인 가능

**모듈 로딩**

- import문을 통해 로드

- 모듈의 코드는 한 번만 실행되고 그 결과는 시스템의 모듈 테이블에 저장

- 동일한 모듈이 다시 import 되면, Python은 새로이 코드를 실행하는 대신 모듈 테이블에서 모듈의 상태를 불러온다

```javascript
import pandas as pd
import my_module
from my_module import a, b, c
from a.b.my_module import hello
```

**import문 내부 동작**

1. Python은 `sys.modules`를 체크

- 이미 로드된 모듈의 이름을 키, 해당 모듈의 객체를 값으로 가진다

- 모듈이 이미 `sys.modules`에 존재하면 그것이 반환

2. `sys.modules`에 없다면 `sys.path`에 있는 디렉토리들을 순회하며 모듈이 있는지 탐색

- `sys.path`는 Python 인터프리터에서 모듈을 어디에서 찾는지 알려주는 경로 리스트

- 스크립트가 실행되는 현재 디렉토리

- `PYTHONPATH` 환경 변수에 명시된 모든 디렉토리

- Python 설치 시 지정된 라이브러리 디렉토리
-  (윈도우는 `“C:\PythonXX\Lib”`와 같은 형태로 저장)

- 각각의 [.egg](https://docs.fileformat.com/ko/programming/egg/) 파일(있을 경우)

- `sys.path`에 디렉토리를 동적으로 추가한 경우, Python은 그 디렉토리를 검색하여 모듈을 찾을 수 있다

- `stie-package`는 `oth`파일을 참고

3. Python이 모듈을 찾았다면 모듈의 코드를 읽고 실행

- 모듈의 이름이 sys.modules에 추가, 이 이름으로 모듈 참조

```javascript
# sampletest.py
name = 'hojun'
```

```javascript
import sampletest
sampltest.name
```

4. 만약 Python 모듈을 찾지 못했다면 `ModuleNotFoundError`가 발생

**모듈 실행**

- 모듈이 로드되면 최상위 레벨에서 실행

- 모듈의 네임스페이스를 정의하는데 사용되는 이름들은 생성

- 모듈의 네임스페이스는 import문을 통해 접근 가능

## 패키지

---

- 파이썬 모듈을 계층적으로 관리할 수 있도록 해준다

```javascript
game/
    __init__.py
    sound/
        __init__.py
        echo.py
        wav.py
    graphic/
        __init__.py
        screen.py
        render.py
    play/
        __init__.py
        run.py
        test.py
```

### 패키지 만들기

```javascript
C:/doit/game/__init__.py
C:/doit/game/sound/__init__.py
C:/doit/game/sound/echo.py
C:/doit/game/graphic/__init__.py
C:/doit/game/graphic/render.py
```

1. `C:/doit` 디렉토리 밑에 game 및 기타 서브 디렉토리 생성, .py 파일을 생성

2. 각 디렉토리에 `__init__.py` 파일을 만들어놓고 비워둔다

3. game 패키지를 참조할 수 있도록 `PYTHONPATH` 환경 변수에 `C:/doit` 디렉토리를 추가

### 패키지 내부 함수 실행

```javascript
# echo.py
def echo_test():
    print("echo")
```

```javascript
# render.py
def render_test():
    print("render")
```

```javascript
# 방법 1 - echo 모듈은 echo.py 파일
import game.sound.echo
game.sound.echo.echo_test()

# 방법 2 - echo 모듈에 있는 디렉토리까지 from … import 하여 실행하는 방법
from game.sound import echo
echo.echo_test()

# 방법 3 - echo 모듈의 echo_test 함수를 직접 import
from game.sound.echo import echo_test
echo_test()
```

### **init**.py의 용도

- 해당 디렉토리가 패키지의 일부임을 알려주는 역할

- `__init__.py` 파일이 없으면 패키지로 인식되지 않음 (Python 3.3버전부터는 생략 가능)

**패키지 변수 및 함수 정의**

```javascript
# C:/doit/game/__init__.py
VERSION = 3.5

def print_version_info():
    print(f"The version of this game is {VERSION}.")
```

**패키지 내 모듈을 미리 import**

```javascript
# C:/doit/game/__init__.py
from .graphic.render import render_test

VERSION = 3.5

def print_version_info():
    print(f"The version of this game is {VERSION}.")
```

**패키지 초기화**

```javascript
# C:/doit/game/__init__.py
from .graphic.render import render_test

VERSION = 3.5

def print_version_info():
    print(f"The version of this game is {VERSION}.")

# 여기에 패키지 초기화 코드를 작성한다.
print("Initializing game ...")
```

**all**

```javascript
__all__ = ['echo']
```

- `import *`를 import했을 때 사용하고 싶은 모듈을 설정

- `__init__.py` 파일에 `__all__` 변수를 설정하고 import할 수 있는 모듈을 정의

## 모듈과 패키지의 재사용성

---

**재사용성의 중요성**

- 코드의 재사용성을 높이는 것은 개발 시간을 줄이고, 코드의 품질을 향상시키는 데 중요

- 재상요성이 높은 코드는 수정이 용이, 에러를 줄일 수 있음

- Python의 모듈과 패키지 시스템은 코드의 재사용성을 크게 향상

**모듈의 재사용성**

- 모듈: Python 코드를 포함하는 .py 파일 → 함수, 변수, 클래스 등을 정의 / import로 사용

- 특정 기능을 수행하는 함수를 여러 스크립트에서 사용해야하는 경우 import해서 사용

**패키지의 재사용성**

- 패키지는 관련된 여러 모듈을 하나의 디렉토리에 모아놓은 것

- 패키지를 통해 모듈을 논리적으로 그룹화, 재사용 가능

- 여러 모듈에서 공통적으로 사용하는 클래스나 함수를 `utilities`라는 패키지에 모아놓고 improt하여 사용할 수 있다

**모듈과 패키지의 재사용성 향상**

- **모듈과 패키지의 설계**: 재사용성을 높이려면 모듈과 패키지를 잘 설계해야한다 → 그룹화하는 것이 중요

- **명확한 API 제공**: 모듈과 패키지는 재사용성을 위해 명확한 API를 제공 → 외부에서 사용할 수 있는 함수, 클래스, 변수를 명확하게 정의하고 사용법을 문서화

- **테스트 코드 작성**: 모듈과 패키지의 재사용성을 높이려면 이를 검증하는 테스트 코드가 필

For the site tree, see the [root Markdown](https://slashpage.com/yerim-devnote.md).
