Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 

README.md

step09_module — 모듈 · 패키지 · 표준 라이브러리

학습 순서 9번. 새로 작성한 챕터이자 기초 과정의 마지막입니다.

왜 여기가 중요한가

지금까지 모든 코드를 한 파일에 썼습니다. 실제 프로젝트는 여러 파일로 나뉘고, 그 대부분은 남이 이미 만들어 둔 것을 가져다 씁니다.

import 를 제대로 이해하면 numpy·pandas도 "특별한 마법"이 아니라 누가 만들어 둔 모듈로 보입니다. 그리고 표준 라이브러리를 알면 직접 짤 필요가 없는 코드를 짜느라 시간을 버리지 않습니다.

학습 목표

  • 모듈(파일)과 패키지(폴더)를 만들고 import 한다
  • import 네 가지 방식의 차이와 각각의 쓸 자리를 안다
  • __init__.py 와 __name__ == '__main__' 의 역할을 안다
  • 표준 라이브러리로 직접 짰던 코드를 대체한다 — Counter·defaultdict·statistics·json·pathlib

파일 지도

경로 내용
mymodule/__init__.py ★ 패키지 표시 + 자주 쓰는 것 미리 꺼내기
mymodule/calc.py 계산 함수 모듈 + 자체 점검
mymodule/text.py 문자열 함수 모듈
moduleEx1.py ★ import 4가지, __name__, 밑줄 관례
stdlibEx.py ★★ datetime·collections·statistics·json·pathlib·os·math·itertools
test1.py 연습문제 — step08 의 CSV를 표준 라이브러리로 재집계
output/ test1.py 결과 (.gitignore 처리)

실행 방법

cd step09_module          # ← 상대 경로를 쓰므로 이 폴더에서 실행하세요
python3 moduleEx1.py
python3 stdlibEx.py
python3 test1.py

python3 mymodule/calc.py  # 모듈을 직접 실행하면 자체 점검이 돈다

짚고 갈 지점

모듈과 패키지

  • 모듈 = .py 파일 하나
  • 패키지 = 모듈을 담은 폴더 (__init__.py 가 있으면 패키지로 인식)
mymodule/          ← 패키지
    __init__.py
    calc.py        ← 모듈
    text.py        ← 모듈

import 4가지

import mymodule.calc                      # 1) 전체. mymodule.calc.add(3,4)
import mymodule.calc as calc              # 2) 별명. calc.add(3,4)
from mymodule.calc import add, average    # 3) 콕 집어서 ★ 가장 많이 씁니다
from mymodule.calc import *               # 4) 전부 — 권장하지 않습니다

4번을 피하는 이유: 어떤 이름이 딸려 왔는지 알 수 없어, 내 변수와 부딪혀도 눈치채기 어렵습니다.

from random import *
choice = '내 선택'          # random.choice 를 덮어썼다. 나중에 choice([1,2]) 하면 터진다

step03_controlstmt/randomEx.py 가 정확히 from random import * 를 씁니다. 그 챕터 README에서 "실무에서는 피한다"고 적어 둔 이유가 이것입니다.

__init__.py — step01~03 의 그 빈 파일

step01_base·step02_operator·step03_controlstmt 에는 내용이 0바이트인 __init__.py 가 들어 있습니다. PyCharm에서 "Python Package"로 폴더를 만들면 자동으로 생기는 파일이라, 원본 수업에서는 아무 역할도 하지 않았습니다.

여기서는 실제로 일을 시킵니다.

# mymodule/__init__.py
from .calc import add, sub, average       # . 은 '이 패키지 안에서'
from .text import count_words, initials

이렇게 해 두면 쓰는 쪽이 짧아집니다.

from mymodule import add                  # mymodule.calc 까지 안 써도 된다
from mymodule.calc import add             # 물론 이것도 여전히 됩니다

파이썬 3.3부터는 __init__.py 없이도 폴더를 import 할 수 있습니다(네임스페이스 패키지). 하지만 명시적으로 두는 편이 여전히 일반적입니다.

__init__.py 는 직접 실행하는 파일이 아닙니다. 해 보면 이렇게 됩니다.

$ python3 mymodule/__init__.py
ImportError: attempted relative import with no known parent package

버그가 아닙니다. from .calc import add 의 . 은 "내가 속한 패키지 안에서" 라는 뜻인데, 파일을 직접 실행하면 그 파일은 어느 패키지에도 속하지 않은 최상위 스크립트가 되어 . 이 가리킬 곳이 없어집니다.

. 이 무엇을 뜻하는지 확인하는 가장 확실한 방법이니 한 번 실행해 보세요. calc.py·text.py 는 상대 import 를 쓰지 않아 직접 실행됩니다.

__name__ — 실행이냐 import냐

print(__name__)
# 직접 실행하면      → '__main__'
# import 되면        → 'mymodule.calc'

그래서 이 조건이 "직접 실행할 때만" 을 뜻합니다.

if __name__ == '__main__':
    print('자체 점검 :', add(3, 4))

python3 mymodule/calc.py 로 직접 실행하면 점검이 출력되고, moduleEx1.py 에서 import 할 때는 조용합니다. 모듈 하나에 "라이브러리 역할"과 "실행 스크립트 역할"을 함께 담는 표준 관용구입니다.

step07_class/calculator.py 도 이걸 씁니다. 없었다면 from calculator import Calculator 하는 순간 입력 프롬프트가 떴을 겁니다.

밑줄로 시작하면 * 에 안 딸려온다

def _internal_helper():     # from mymodule.calc import * 로는 안 온다
    ...

step07_class 의 self._balance 와 같은 "밖에서 쓰지 마세요" 관례입니다. 강제는 아니고 mymodule.calc._internal_helper() 라고 직접 쓰면 됩니다.

표준 라이브러리 — 직접 짰던 것들이 이미 있습니다

직접 짰던 코드 표준 라이브러리
step05 개수 세기 counter.get(c,0)+1 collections.Counter
step08 그룹 묶기 setdefault(k, []).append(v) collections.defaultdict(list)
sum(x)/len(x) 평균 statistics.mean (중앙값·표준편차도)
경로 문자열 이어 붙이기 pathlib.Path — Path('a') / 'b' / 'c.csv'
Counter(words).most_common(2)      # 많이 나온 순 상위 2개 — 한 줄

step05_datastructure/test2.py 에서 손으로 짰던 단어 빈도 + 정렬이 이 한 줄입니다.

직접 짜 본 다음에 도구를 아는 순서가 중요합니다. 도구부터 배우면 그게 무엇을 대신해 주는지 모르고, 조금만 달라져도 손을 못 댑니다.

json — 데이터를 주고받는 표준 형식

json.dumps(data, ensure_ascii=False, indent=2)    # 객체 → 문자열
json.loads(text)                                  # 문자열 → 객체

ensure_ascii=False 를 빠뜨리면 한글이 홍길동 처럼 나옵니다. 거의 항상 붙이세요.

JSON은 파이썬 딕셔너리와 모양이 거의 같습니다. 웹 API 응답이 대부분 이 형식이라, 데이터를 가져올 때 계속 만납니다.

파일 이름 규칙에 관해

이 저장소의 파일 이름은 dataType1.py·ifEx1.py·moduleEx1.py 처럼 camelCase 입니다. 원본 수업이 그렇게 썼고, 새 챕터도 저장소가 하나로 읽히도록 맞췄습니다.

하지만 파이썬 표준(PEP 8)은 모듈 이름에 소문자 snake_case 를 권합니다.

data_type1.py    if_ex1.py    module_ex1.py     ← PEP 8 스타일

이유가 있습니다. 모듈 이름은 곧 import 할 때 쓰는 이름인데, 대소문자를 구분하지 않는 파일 시스템(윈도우·기본 macOS)에서 대소문자가 섞이면 문제가 생깁니다. 실제로 이 저장소에도 루트에 ifEX1.py, 챕터에 ifEx1.py 가 공존해 헷갈렸던 적이 있습니다.

  • 모듈·변수·함수 → snake_case
  • 클래스 → PascalCase (Calculator, Student — step07 참고)
  • 상수 → UPPER_CASE (SUBJECTS, CSV_PATH — step08·step09 참고)

새 챕터의 함수·변수·상수는 PEP 8을 따랐습니다. 파일 이름만 기존 관습을 유지했습니다.

연습 과제

  1. mymodule 에 stats.py 를 추가해 평균·중앙값·최빈값 함수를 만들고, __init__.py 에서 꺼내 쓰세요.
  2. mymodule/text.py 를 직접 실행(python3 mymodule/text.py)하고, moduleEx1.py 에서 import 했을 때와 무엇이 다른지 확인하세요.
  3. step05_datastructure/test2.py 의 단어 빈도를 collections.Counter 로 다시 쓰세요. 몇 줄이 줄어드나요?
  4. step08_file/test1.py 의 setdefault 그룹 묶기를 defaultdict 로 바꾸세요.
  5. stdlibEx.py 의 datetime 예제를 고쳐, 오늘부터 크리스마스까지 며칠 남았는지 출력하세요.
  6. test1.py 가 만든 output/summary.json 을 다시 읽어(json.load) 학년별 평균만 출력하는 스크립트를 만드세요.
  7. itertools.combinations 로 step03 의 로또 번호 45개 중 6개 조합이 몇 가지인지 세어 보세요. (힌트: 세다가 오래 걸립니다. math.comb 를 쓰면 즉시 나옵니다)

여기까지 왔다면

기초 문법은 끝났습니다. 다음은 step10_numpy 와 step11_pandas 입니다.

⚠️ 여기서부터 설치가 필요합니다. step01~step09 는 파이썬만 있으면 됐지만, step10 부터는 NumPy·pandas를 씁니다. 루트 requirements.txt 를 참고하세요.

지금 할 수 있는 것 다음 단계에서 만날 것
csv.DictReader 로 파일 읽기 pandas.read_csv() — 한 줄로, 타입 변환까지
for 로 한 줄씩 순회 DataFrame — 열 단위 연산
setdefault/defaultdict 로 묶기 groupby()
statistics.mean df.mean(), df.describe()
숫자를 눈으로 읽기 matplotlib — 그래프

이 대응표가 보인다면 이 챕터들의 목적은 달성된 것입니다. pandas는 여기서 손으로 한 일을 짧게 해 주는 도구이지, 새로운 세계가 아닙니다.

step11_pandas/groupEx.py 첫 절에는 step08 방식과 groupby 를 나란히 실행해 결과가 같음을 확인하는 코드가 있습니다.