모듈을 활용한 추상화를 예제를 이용하여 설명한다.
프로그램과 라이브러리를 모듈 단위로 나누어 관리하는 것을 모듈식 프로그래밍(modular programming)이라 부른다.
파이썬에서 모듈은 간단하게 말하면 하나의 파이썬 소스코드 파일이다.
파일의 확장자가 py
이며,
모듈에는 서로 연관된 기능을 수행하는 함수, 상수, 변수, 클래스, 객체 등이 저장된다.
모듈에 포함된 대상을 사용하려면 모듈이름과 함께 점(.
)을 다음과 같이 활용한다.
예를 들어,
데이터셋 다루기에서
urllib.request
모듈에 포함된 urlopen
함수를
이용하여 인터넷 상에 있는 파일 내용을 가져왔다.
이 때 사용한 형식은 다음과 같다.
urllib.request.urlopen(fileURL)
이렇게 작성한 이유는 urllib.request
는 모듈이며, 그 안에 urlopen
함수가 정의되어 있기 때문이다.
물론 그 전에 urllib.request
모듈을 아래와 같이 불러왔다.
import urllib.request
모듈을 먼저 불러오지 않으면 모듈 내에서 정의된 대상을 사용할 수 없다.
이렇게 서로 연관된 기능을 수행하는 여러 대상을 모듈로 묶어 놓고 대상의 정의는 알 필요 없이 기능만 알면 사용할 수 있도록 만든다는 의미에서 모듈 또한 코드 추상화 기법으로 사용된다.
모듈은 크게 세 종류로 나뉜다.
math
, urllib.request
, random
, turtle
, os
, sys
등등numpy.random
, matplotlib.pyplot
, pygame.mixer
등등myWget
함수의 정의가 포함된 파일 wgetFiles.py
,
챕터별로 작성한 프로그램을 담은 소스코드 파일 등등 패키지는 모듈을 계층적으로 관리할 수 있게 도와주는 체계이다. 보통 관련된 여러 개의 모듈을 하나의 디렉토리(폴더)에 담는 형식으로 패키지를 생성한다. 라이브러리는 하나의 패키지 또는 관련된 패키지의 모음을 가리킨다.
예를 들어, urllib.request
모듈은 urllib
패키지(디렉토리)에 포함되어 있다.
패키지와 모듈은 점(.
)으로 구분된다.
또한 간단한 그래프 관련 도구(함수, 클래스, 객체 등)가 정의되어 있는 matplotlib.pyplot
은
matplotlib
이라는 패키지 않에 포함되어 있으며,
게임 프로그래밍을 위해 가장 많이 사용되는 패키지는 pygame
이며,
그 안에서 소리(사운드) 관련 도구가 포함된 모듈이 mixer
이다.
__init__.py
파일¶특정 디렉토리가 파이썬 패키지임을 명시하려면
디렉토리 안에 __init__.py
라는 파일을 저장해 두어야 한다.
그렇게 하면 파이썬 패키지로서 아래와 같은 기능을 수행하게 만들 수 있다.
예를 들어, 파이썬 표준 라이브러리에 포함된 tkinter
패키지는
그래픽 유저 인터페이스(GUI)와 관련된 다양한 도구를 제공한다.
그런데 tkinter
패키지를 불러오면 __init__.py
에서
정의된 설정에 따라 다른 모듈을 굳이 불러올 필요 없이 많은 도구들을 바로 사용할 수 있다.
tkinter
패키지의 내용은
tkinter 소스코드에서
확인할 수 있다.
카페 두 곳에서 사용하는 POS(Point-Of-Sale)기에서 주문을 받아 계산 영수증을 생성하는 프로그램을 모듈을 활용하여 구현한다.
아래 프로그램은 파이썬 카페의 POS기에 사용되는 프로그램이다.
손님이 선택하는 메뉴를 고를 때마다 개수와 가격을 저장한 후에
합산된 가격을 알려주는 동시에
주문 내용과 가격을 receipt.txt
파일에 저장한다.
주의사항
receipt.txt
파일을 codes
라는 하위폴더에 저장한다.# 메뉴와 가격 리스트
itemsPython = {"Donut":2000, "Cafe Latte":4000, "Americano":3500, "Espresso":3000}
# 주문내역을 영수증 파일에 저장하는 함수
def saveReceipt(items, selections, price):
with open("./codes/receipt.txt", "w") as receipt:
for item in selections: # 선택된 항목 나열
receipt.write(f'{item:>10}\t{items[item]:5}\t{selections[item]:2}\n')
receipt.write("==========\n")
receipt.write(f' Total\t{price}\n') # 가격 합계 표시
print("점원: 아래 메뉴 중에서 고르세요")
print("============")
for item in itemsPython:
print(f"{item:>10}: {itemsPython[item]}원") # 메뉴 오른쪽 정렬
print("============")
# 손님이 주문하기
running = True # 아래 while 문이 실행되는 조건
selected = {} # 선택한 메뉴 및 개수 목록
totalPrice = 0 # 가격 합계
while running:
choice = input("점원: 선택하실래요? ")
if choice == "N": # 더 이상 선택하지 않을 경우
running = False # 주문 종료
print("점원: 알겠습니다.")
else: # 목록에 있는 메뉴를 선택한 경우
count = int(input("점원: 몇 개 드릴까요"))
selected[choice] = count # 선택 메뉴 및 개수 추가
totalPrice += itemsPython[choice] * count # 가격 합계 계산
# 주문내역 저장하기
saveReceipt(itemsPython, selected, totalPrice)
print(f'다 합해서 {totalPrice}원입니다.')
위 코드의 실행결과로 receipt.txt
파일에 아래 내용으로 저장된다.
with open("./codes/receipt.txt", "r") as receipt:
for line in receipt:
print(line,end='') # 사용된 서식 그대로 출력
아래 프로그램은 아나콘다 커피의 POS에서 사용되는 프로그램이다.
전제조건
receipt.txt
파일을 사용한다.따라서 아래 프로그램은 메뉴와 가격만이 다를 뿐 나머지는 동일하다.
# 메뉴와 가격 리스트
itemsAnaconda = {"Bagle":2500, "Caffuccino":4000, "Americano":3500, "Affogato":4500}
# 주문내역을 영수증 파일에 저장하는 함수
def saveReceipt(items, selections, price):
with open("./codes/receipt.txt", "w") as receipt:
for item in selections:
receipt.write(f'{item:>10}\t{itemsAnaconda[item]:5}\t{selections[item]:2}\n')
receipt.write("==========\n")
receipt.write(f' Total\t{price}\n')
print("점원: 아래 메뉴 중에서 고르세요")
print("============")
for item in itemsAnaconda:
print(f"{item:>10}: {itemsAnaconda[item]}원")
print("============")
# 손님이 주문하기
running = True
selected = {}
totalPrice = 0
while running:
choice = input("점원: 선택하실래요? ")
if choice == "N":
running = False
print("점원: 알겠습니다.")
else:
count = int(input("점원: 몇 개 드릴까요"))
selected[choice] = count
totalPrice += itemsAnaconda[choice] * count
# 주문내역 저장하기
saveReceipt(itemsAnaconda, selected, totalPrice)
print(f'다 합해서 {totalPrice}원입니다.')
위 코드의 실행결과로 receipt.txt
파일에 아래 내용으로 저장된다.
with open("./codes/receipt.txt", "r") as receipt:
for line in receipt:
print(line,end='')
파이썬 카페의 POS와 아나콘다 커피의 POS는 거래내역을
codes
디렉토리에 receipt.txt
파일로 저장한다.
그런데 두 코드는 saveReceipt
함수를 동일하게 사용한다.
saveReceipt
함수를 codes
디렉토리에 receipt.py
라는 파일에 저장하여
두 개의 POS 에서 공유하도록 해보자.
주의사항
codes
디렉토리에 __init__.py
파일을 생성하자. 일단은 아무런 내용이 없어도 된다.receipt.py
모듈의 내용으로 아래 그림과 같이 saveReceipt
함수의 정의만 추가한다.
이제 두 매장에서 사용하는 코드는 아래와 같이 모듈을 불러오는 형식으로 구현될 수 있다.
receipt
모듈에 포함되어 있는 모든 함수 불러오기from codes.receipt import *
receipt
모듈에 포함되어 있는 특정 함수만 불러오기from codes.receipt import saveReceipt
만약에 receipt.py
파일이 현재 작업디렉토리에 있거나
프로그래밍 기본 요소: 모듈과 패키지에서
설명되었듯이 codes
디렉토리가 파이썬 라이브러리 경로에 추가되어 있다면
아래와 같이 불러온다.
from receipt import *
또는
from receipt import saveReceipt
여기서는 라이브러리 경로 설정을 하지 않은 방식으로 모듈을 사용한다.
이제 두 매장에서 사용되는 코드를 좀 더 간단하게 구현할 수 있다.
from codes.receipt import saveReceipt
# 메뉴와 가격 리스트
itemsPython = {"Donut":2000, "Cafe Latte":4000, "Americano":3500, "Espresso":3000}
print("점원: 아래 메뉴 중에서 고르세요")
print("============")
for item in itemsPython:
print(f"{item:>10}: {itemsPython[item]}원")
print("============")
# 손님이 주문하기
running = True
selected = {}
totalPrice = 0
while running:
choice = input("점원: 선택하실래요? ")
if choice == "N":
running = False
print("점원: 알겠습니다.")
else:
count = int(input("점원: 몇 개 드릴까요"))
selected[choice] = count
totalPrice += itemsPython[choice] * count
# 주문내역 저장하기
saveReceipt(itemsPython, selected, totalPrice)
print(f'다 합해서 {totalPrice}원입니다.')
with open("./codes/receipt.txt", "r") as receipt:
for line in receipt:
print(line,end='')
from codes.receipt import saveReceipt
# 메뉴와 가격 리스트
itemsAnaconda = {"Bagle":2500, "Caffuccino":4000, "Americano":3500, "Affogato":4500}
print("점원: 아래 메뉴 중에서 고르세요")
print("============")
for item in itemsAnaconda:
print(f"{item:>10}: {itemsAnaconda[item]}원")
print("============")
# 손님이 주문하기
running = True
selected = {}
totalPrice = 0
while running:
choice = input("점원: 선택하실래요? ")
if choice == "N":
running = False
print("점원: 알겠습니다.")
else:
count = int(input("점원: 몇 개 드릴까요"))
selected[choice] = count
totalPrice += itemsAnaconda[choice] * count
# 주문내역 저장하기
saveReceipt(itemsAnaconda, selected, totalPrice)
print(f'다 합해서 {totalPrice}원입니다.')
with open("./codes/receipt.txt", "r") as receipt:
for line in receipt:
print(line,end='')
멤버십 카드를 가지고 있는 사람들에게 10% 할인혜택을 주고자 한다면 예를 들어 다음 코드를 사용할 수 있다.
def discount_10(price):
return price * 0.1 # 10% 할인
반면에 특별한 날 모든 손님에게 20% 할인혜택을 주고자 할 때도 가격을 낮추는 함수를 사용할 수 있다.
def discount_20(price):
return price * 0.2 # 20% 할인
하지만 위와 같이 함수 이름을 매번 달리 하면서 코딩을 하면 여러 모로 불편하다. 무엇보다도 사실상 동일한 일을 수행하는 함수를 매번 다른 이름을 주면 나중에 프로그램을 관리하거나 수정할 때 불편하다.
그렇다고 해서 아래와 같이 동일한 이름을 사용하면 문제가 발생할 수 있다.
def discount(price):
return price * 0.1 # 10% 할인
def discount(price):
return price * 0.2 # 20% 할인
위와 같이 하면 discount
함수가 호출될 때 가격을 무조건 20% 할인한다.
이런 문제를 해결하기 위해서는 두 함수를 서로 다른 모듈에 저장하는 것이다.
예를 들어, 멤버십 할인함수는 앞서 언급한 receipt.py
모듈에 저장하고
특별한 날 할인행사용 함수는 codes
디렉토리의 special.py
모듈에 저장하도록 하자.
이제 멤버십 할인과 특별한 날 할인행사를 지원하는 코드를 아래와 같이 작성할 수 있다.
special
모듈을 불러올 때 receipt
모듈을 불러오는 방식과는 다른 방식을 사용해야 한다. 즉,
import codes.special
이렇게 하면 special
모듈에 포함된 discount
함수를 호출할 때 모듈이름을 함께 사용해야 한다.
codes.special.discount(price)
codes.special
처럼 긴 이름 대신에 아래와 같이 별칭을 사용하면 보다 편리해진다.
import codes.special as sale
그러면 모듈 안에 있는 대상을 별칭과 함께 사용해야 한다.
sale.discount(price)
주의: 별칭을 지정했으면 반드시 별칭을 사용해야 한다.
아래 코드는 파이썬 카페에서 도넛
을 구입할 때 특별한 날 할인, 멤버십 할인 여부를 확인하여
경우에 따라 동일한 제품을 다른 가격으로 판매되는 것을 보여준다.
주의: 중복할인을 지원하지 않는 코드이다.
# receipt.py 모듈의 모든 함수 불러오기
from codes.receipt import *
# special.py 모듈만 불러오기
import codes.special as sale
# 메뉴와 가격 리스트
itemsPython = {"Donut":2000, "Cafe Latte":4000, "Americano":3500, "Espresso":3000}
print("점원: 아래 메뉴 중에서 고르세요")
print("============")
for item in itemsPython:
print(f"{item:>10}: {itemsPython[item]}원")
print("============")
# 손님이 주문하기
running = True
selected = {}
totalPrice = 0
while running:
choice = input("점원: 선택하실래요? ")
if choice == "N":
running = False
print("점원: 알겠습니다.")
else:
count = int(input("점원: 몇 개 드릴까요"))
selected[choice] = count
totalPrice += itemsPython[choice] * count
# 할인 추가 (중복할인 없음)
if input("특별한 날 확인: ") == "Y":
dc = int(sale.discount(totalPrice))
elif input("점원: 멤버십 카드 있으세요? ") == "Y":
dc = int(discount(totalPrice))
# 주문내역 저장하기
saveReceipt(itemsPython, selected, totalPrice-dc)
print(f'다 합해서 {totalPrice}원에서 {dc}원 할인된 {totalPrice-dc}원입니다.')
with open("./codes/receipt.txt", "r") as receipt:
for line in receipt:
print(line,end='')
# receipt.py 모듈의 모든 함수 불러오기
from codes.receipt import *
# special.py 모듈만 불러오기
import codes.special as sale
# 메뉴와 가격 리스트
itemsPython = {"Donut":2000, "Cafe Latte":4000, "Americano":3500, "Espresso":3000}
print("점원: 아래 메뉴 중에서 고르세요")
print("============")
for item in itemsPython:
print(f"{item:>10}: {itemsPython[item]}원")
print("============")
# 손님이 주문하기
running = True
selected = {}
totalPrice = 0
while running:
choice = input("점원: 선택하실래요? ")
if choice == "N":
running = False
print("점원: 알겠습니다.")
else:
count = int(input("점원: 몇 개 드릴까요"))
selected[choice] = count
totalPrice += itemsPython[choice] * count
# 할인 추가 (중복할인 없음)
if input("특별한 날 확인: ") == "Y":
dc = int(sale.discount(totalPrice))
elif input("점원: 멤버십 카드 있으세요? ") == "Y":
dc = int(discount(totalPrice))
# 주문내역 저장하기
saveReceipt(itemsPython, selected, totalPrice-dc)
print(f'다 합해서 {totalPrice}원에서 {dc}원 할인된 {totalPrice-dc}원입니다.')
with open("./codes/receipt.txt", "r") as receipt:
for line in receipt:
print(line,end='')
codes.receipt
모듈에서 정의되어야 한다.
orderAccept
함수의 명세saveReceipt
함수의 명세./codes/receipt.txt
파일에 주문내역 저장하는 기능 유지.orderAccept
함수를 아래 명세를 만족하도록 한 번 더 수정하라.if
조건문 또는 예외처리 기능을 활용.