15. 모듈과 패키지
- 코드를 여러 파일로 나누고
import로 불러온다. import의 여러 형태(import,from import,as)를 구분한다.- 표준 라이브러리의 유용한 모듈을 맛본다.
- 패키지(폴더 단위)와
if __name__ == "__main__"을 이해한다.
먼저: 모듈이 뭔가요?
지금까지 코드를 한 파일에 다 적었습니다. 하지만 프로그램이 커지면 한 파일이 수천 줄이 되어 관리가 불가능해집니다. 그래서 코드를 여러 파일로 나눕니다.
모듈(module)은 그냥 하나의 .py 파일입니다. 함수·변수·클래스를 담아두고, 다른 파일에서 import로 불러와 씁니다.
사실 우리는 이미 모듈을 써왔습니다. 초급편에서 decimal 모듈을 언급했고, random으로 난수를 만든다는 이야기도 나왔죠. 이들은 Python이 기본 제공하는 모듈입니다. 이제 직접 모듈을 만들고 불러옵니다.
mymath.py라는 파일을 만들어 봅시다.
# mymath.py PI = 3.14159 def add(a, b): return a + b def circle_area(r): return PI * r * r
같은 폴더의 다른 파일(main.py)에서 이를 불러옵니다.
# main.py import mymath print(mymath.add(3, 5)) # 8 print(mymath.PI) # 3.14159 print(mymath.circle_area(2)) # 12.56636
import mymath로 파일을 통째로 가져오고, mymath.add()처럼 모듈이름.기능 형태로 씁니다.
import 할 때 무슨 일이 일어날까
import mymath를 만나면 Python은 세 가지를 합니다.
mymath.py파일을 찾아 한 번 처음부터 끝까지 실행합니다(그 안의def·변수 정의가 메모리에 올라감).- 그 결과를
mymath라는 이름표에 담습니다. - 이후
mymath.add처럼 점으로 그 안의 것에 접근하게 해줍니다.
여기서 중요한 점 두 가지가 있습니다.
- 한 번만 실행됩니다. 같은 모듈을 여러 곳에서 여러 번
import해도, 파일은 처음 한 번만 실행되고 그 결과가 재사용됩니다. 그래서 import는 빠르고, 모듈 안 코드가 매번 반복 실행될까 걱정할 필요가 없습니다. - 이름 공간(namespace)이 분리됩니다.
mymath.PI와 내 파일의PI는 서로 다른 것입니다.mymath.라는 "성(姓)"이 붙어 있어, 이름이 겹쳐도 충돌하지 않습니다. 이것이import 모듈방식이 안전한 이유입니다.
print("로딩됨")을 넣어두면 그 모듈이 처음 import될 때 딱 한 번만 출력됩니다. (이 특성은 뒤에 나올 if __name__ == "__main__"과 연결됩니다.).py 파일이다. import로 불러와 코드를 재사용하고 파일을 깔끔하게 나눈다. import된 모듈은 한 번만 실행되며, 모듈.이름으로 접근해 이름 충돌을 피한다.flowchart LR
Main["main.py"]:::proc -->|import| MyMath["mymath.py<br/>add, PI, circle_area"]:::data
Main -->|import| Random["random<br/>(표준 라이브러리)"]:::data
Main -->|import| Math["math<br/>(표준 라이브러리)"]:::data
classDef proc fill:#7fd8d8,stroke:#2a9d8f,color:#14532d
classDef data fill:#a8dadc,stroke:#457b9d,color:#1d3557
import의 여러 형태
불러오는 방법은 세 가지가 있습니다. 상황에 맞게 고릅니다.
1) import 모듈 — 통째로
import mymath print(mymath.add(3, 5)) # 항상 mymath. 을 붙여 씀
가장 안전합니다. mymath.add처럼 어디서 왔는지 분명히 드러납니다.
2) from 모듈 import 이름 — 골라서
특정 기능만 콕 집어 가져오면, 모듈 이름 없이 바로 쓸 수 있습니다.
from mymath import circle_area, PI print(circle_area(2)) # mymath. 없이 바로 print(PI) # 3.14159
3) import 모듈 as 별명 — 별명 붙이기
긴 모듈 이름을 짧게 줄일 때 씁니다. 데이터 분석에서 import pandas as pd가 대표적입니다.
import mymath as mm print(mm.add(10, 20)) # 30
| 형태 | 사용 | 언제 |
|---|---|---|
import 모듈 | 모듈.기능() | 기본. 출처가 분명 |
from 모듈 import 기능 | 기능() | 자주 쓰는 몇 개만 |
import 모듈 as 별명 | 별명.기능() | 이름이 길 때 |
from 모듈 import *: 별표로 모든 것을 가져오는 방식은 피하세요.```python
from mymath import * # ❌ 비권장
```
무엇이 들어왔는지 알 수 없어 이름이 충돌하거나 코드를 읽기 어려워집니다. 필요한 것을 명시적으로 가져오는 게 파이썬답습니다.
표준 라이브러리 맛보기
Python에는 표준 라이브러리(standard library)라는 방대한 모듈 모음이 기본 포함돼 있습니다. 설치 없이 import만 하면 바로 씁니다. "건전지 포함(batteries included)"이라는 Python의 철학이죠.
자주 쓰는 몇 가지를 봅시다.
import math print(math.sqrt(16)) # 4.0 (제곱근) print(math.pi) # 3.141592653589793 print(math.ceil(4.1)) # 5 (올림) import random print(random.randint(1, 6)) # 1~6 주사위 print(random.choice(["가위","바위","보"])) # 무작위 선택 from datetime import date print(date.today()) # 오늘 날짜 print(date(2025, 12, 31)) # 2025-12-31 from collections import Counter print(Counter("banana")) # Counter({'a': 3, 'n': 2, 'b': 1}) 빈도 세기
| 모듈 | 무엇에 쓰나 | 대표 기능 |
|---|---|---|
math | 수학 계산 | sqrt, pi, ceil, floor |
random | 난수·무작위 | randint, choice, shuffle |
datetime | 날짜·시간 | date.today(), datetime.now() |
collections | 특수 자료구조 | Counter, defaultdict, deque |
json | JSON 처리 | load, dump (16장) |
csv | CSV 처리 | reader, writer (16장) |
os·pathlib | 파일 경로·시스템 | 경로 조작, 폴더 탐색 |
Counter), 날짜 계산(datetime) 등은 직접 짜는 것보다 표준 모듈이 정확하고 빠릅니다. 전체 목록은 docs.python.org에서 볼 수 있습니다.패키지: 모듈을 폴더로 묶기
모듈(파일)이 많아지면 패키지(package)로 묶습니다. 패키지는 관련 모듈들을 담은 폴더입니다. 라이브러리가 커지면 보통 이 구조를 띱니다.
shop/ ← 패키지(폴더) ├── __init__.py ← "이 폴더는 패키지다"를 표시 ├── cart.py ← 모듈 └── discount.py ← 모듈
# shop/cart.py def total(prices): return sum(prices) # shop/discount.py def apply(price, rate): return price * (1 - rate)
다른 파일에서 점(.)으로 패키지 안 모듈에 접근합니다.
from shop.cart import total from shop import discount print(total([1000, 2000, 3000])) # 6000 print(discount.apply(10000, 0.2)) # 8000.0
📎 __init__.py는 "이 폴더를 패키지로 취급하라"는 표시 파일입니다. 비어 있어도 됩니다. (최신 Python은 없어도 동작하는 경우가 있지만, 명시적으로 두는 것이 안전하고 표준적입니다.)
⭐ if __name__ == "__main__"
모듈을 만들다 보면 이상한 코드를 자주 봅니다.
if __name__ == "__main__": ...
이게 무슨 뜻일까요? __name__은 Python이 자동으로 설정하는 특별한 변수입니다.
- 파일을 직접 실행하면 (
python mymath.py) →__name__은"__main__" - 파일을 다른 곳에서 import하면 →
__name__은 모듈 이름("mymath")
이 차이를 이용해, "직접 실행할 때만 돌릴 코드"를 구분합니다.
# mymath.py def add(a, b): return a + b if __name__ == "__main__": # 직접 실행할 때만 실행됨 print("테스트:", add(2, 3)) # import 할 땐 실행 안 됨
# 직접 실행 $ python mymath.py 테스트: 5 # 다른 파일에서 import — 위 print는 실행되지 않음 import mymath # 아무것도 출력 안 됨 (add 함수만 가져옴)
if __name__ == "__main__": 안의 코드는 그 파일을 직접 실행할 때만 돌고, import될 때는 무시된다. 모듈에 테스트나 실행 코드를 넣되 import 시 방해하지 않게 하는 표준 관용구다.이 패턴이 중요한 이유: 모듈을 import하는 쪽에서는 그 모듈의 함수만 쓰고 싶지, 그 안의 실행 코드가 멋대로 돌아가는 건 원치 않기 때문입니다.
나쁜 예 ❌ vs 좋은 예 ✅
# ❌ 나쁜 예: 한 파일에 모든 것 (수천 줄, 관리 불가) # everything.py 에 함수 200개, 클래스 50개... # ✅ 좋은 예: 역할별로 모듈 분리 # models.py ← 데이터 클래스들 # utils.py ← 보조 함수들 # main.py ← from models import User; from utils import format_date
이 장에서 배운 것
- 모듈은 함수·클래스를 담은
.py파일이다.import로 불러와 재사용한다. - import 형태 셋:
import 모듈(출처 분명),from 모듈 import 기능(골라서),import 모듈 as 별명(이름 단축).import *는 피한다. - 표준 라이브러리(
math·random·datetime·collections등)는 설치 없이 바로 쓴다. 직접 만들기 전에 먼저 찾아본다. - 패키지는 모듈을 담은 폴더이며
__init__.py로 표시한다. 점(.)으로 내부 모듈에 접근한다. if __name__ == "__main__":은 파일을 직접 실행할 때만 도는 코드를 구분한다.
🧪 실습 문제
문제 1. greetings.py 모듈에 hello(name) 함수("안녕, {name}!" 반환)를 만들고, 다른 파일에서 from greetings import hello로 불러와 hello("민지")를 출력하는 코드를 쓰세요. (코드만 작성)
문제 2. import math를 이용해 144의 제곱근과, 7을 올림한 값(math.ceil)을 출력하세요.
문제 3. 다음 중 __name__이 "__main__"이 되는 경우는?
(a) python mymodule.py 로 직접 실행할 때 (b) 다른 파일에서 import mymodule 할 때
문제 4. random 모듈로 1부터 45 사이의 서로 다른 로또 번호 6개를 뽑아 정렬해 출력하세요. (힌트: random.sample(범위, 개수))
문제 5. from collections import Counter로, 문자열 "mississippi"에서 각 글자의 빈도를 출력하세요.
<details>
<summary>✅ 정답·해설 보기</summary>
1.
# greetings.py def hello(name): return f"안녕, {name}!" # main.py from greetings import hello print(hello("민지")) # 안녕, 민지!
2.
import math print(math.sqrt(144)) # 12.0 print(math.ceil(7)) # 7 (이미 정수라 그대로; math.ceil(7.1)이면 8)
3. (a)만. 직접 실행하면 "__main__", import하면 모듈 이름("mymodule")이 됩니다.
4.
import random numbers = sorted(random.sample(range(1, 46), 6)) print(numbers) # 예: [3, 12, 19, 27, 33, 41] (실행마다 다름)
(random.sample은 중복 없이 뽑아줍니다.)
5.
from collections import Counter print(Counter("mississippi")) # Counter({'i': 4, 's': 4, 'p': 2, 'm': 1})
</details>
◀️ 이전 장: 14. 이터레이터와 제너레이터 | ▶️ 다음 장: 16. 파일 입출력과 데이터 형식