함수 호출할 때마다 모든 매개변수를 다 써야 한다면 불편하겠죠? 🤔 ──자주 쓰는 값은 “기본값”으로 설정하고, 필요할 때만 바꿀 수 있습니다! 😊
print()를 쓸 때 end="\n"을 매번 안 쓰는 이유가 뭘까요? 이미 기본값으로 설정되어 있기 때문입니다!
오늘은 우리 함수도 이렇게 편리하게 만드는 법을 배웁니다. 선택적 매개변수로 유연한 함수를 디자인하는 실전 기법! 💡
(30분 완독 ⭐⭐⭐)
🎯 오늘의 학습 목표
- 기본 매개변수로 선택적 값 받기
- 키워드 인자로 명확하게 전달하기
- 키워드 인자의 장점 활용하기
- 위치/키워드 인자 순서 규칙 이해하기
- 가변 기본값 함정 피하기
- 실전 함수 설계 패턴 익히기
📚 사전 지식
🎯 학습 목표 1: 기본값 매개변수 설정하기
한 줄 설명
기본 매개변수 = 함수 정의 시 매개변수에 미리 설정해둔 기본값
매개변수에 값을 전달하지 않으면 자동으로 기본값이 사용됩니다.
실생활 비유
1
2
3
4
5
6
7
8
9
| 📱 전화기 설정:
벨소리 설정(소리="기본벨소리", 볼륨=50)
- 소리를 지정하지 않으면? → 기본벨소리 사용
- 볼륨을 지정하지 않으면? → 50으로 설정
커피 주문:
주문(종류="아메리카노", 사이즈="미디엄", 샷="2개")
- 종류만 말하면? → 미디엄 사이즈, 2샷으로 자동 주문
|
🎯 학습 목표 2: 키워드 전용 인자 사용하기
기본 문법
1
2
| def 함수이름(매개변수=기본값):
실행할 코드
|
첫 번째 예제
1
2
3
4
5
6
7
8
9
| def greet(name="손님"):
"""이름을 받아 인사하는 함수 (기본값: 손님)"""
print(f"안녕하세요, {name}님!")
# 값을 전달하면 전달한 값 사용
greet("홍길동") # 안녕하세요, 홍길동님!
# 값을 전달하지 않으면 기본값 사용
greet() # 안녕하세요, 손님님!
|
여러 개의 기본값
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| def introduce(name, age=0, city="미정"):
"""자기소개 함수 (나이와 도시는 선택사항)"""
print(f"이름: {name}")
print(f"나이: {age}세")
print(f"도시: {city}")
# 모든 값 전달
introduce("홍길동", 25, "서울")
# 이름: 홍길동
# 나이: 25세
# 도시: 서울
# 이름만 전달 (나머지는 기본값)
introduce("김철수")
# 이름: 김철수
# 나이: 0세
# 도시: 미정
# 이름과 나이만 전달
introduce("이영희", 30)
# 이름: 이영희
# 나이: 30세
# 도시: 미정
|
실전 예제 1: 계산기 함수
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| def calculate(a, b, operation="+"):
"""두 숫자를 계산하는 함수 (기본 연산: 덧셈)"""
if operation == "+":
return a + b
elif operation == "-":
return a - b
elif operation == "*":
return a * b
elif operation == "/":
return a / b if b != 0 else "0으로 나눌 수 없음"
else:
return "지원하지 않는 연산"
# 기본 연산(덧셈) 사용
print(calculate(10, 5)) # 15
# 연산 지정
print(calculate(10, 5, "-")) # 5
print(calculate(10, 5, "*")) # 50
print(calculate(10, 5, "/")) # 2.0
|
실전 예제 2: 파일 읽기 함수
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| def read_file(filename, encoding="utf-8", mode="r"):
"""파일을 읽는 함수 (기본: UTF-8, 읽기 모드)"""
try:
with open(filename, mode, encoding=encoding) as f:
return f.read()
except FileNotFoundError:
return f"파일을 찾을 수 없습니다: {filename}"
# 기본 설정으로 읽기
content = read_file("data.txt")
# 다른 인코딩으로 읽기
content = read_file("data.txt", "cp949")
# 바이너리 모드로 읽기
content = read_file("image.png", mode="rb")
|
실전 예제 3: 사용자 프로필 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| def create_profile(name, age=18, city="서울", job="학생", hobby="독서"):
"""사용자 프로필을 생성하는 함수"""
profile = {
"이름": name,
"나이": age,
"도시": city,
"직업": job,
"취미": hobby
}
return profile
# 최소 정보만 제공
profile1 = create_profile("홍길동")
print(profile1)
# {'이름': '홍길동', '나이': 18, '도시': '서울', '직업': '학생', '취미': '독서'}
# 일부 정보 제공
profile2 = create_profile("김철수", 25, "부산")
print(profile2)
# {'이름': '김철수', '나이': 25, '도시': '부산', '직업': '학생', '취미': '독서'}
|
🎯 학습 목표 2: 키워드 전용 인자 사용하기
한 줄 설명
키워드 인자 = 매개변수 이름을 명시해서 값을 전달하는 방식
순서에 상관없이 명확하게 값을 전달할 수 있습니다.
위치 인자 vs 키워드 인자
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| def book_info(title, author, year):
print(f"제목: {title}")
print(f"저자: {author}")
print(f"출판년도: {year}")
# 위치 인자 (Positional Arguments)
# - 순서가 중요함
book_info("Python 입문", "홍길동", 2024)
# 키워드 인자 (Keyword Arguments)
# - 순서가 중요하지 않음
book_info(title="Python 입문", author="홍길동", year=2024)
book_info(author="홍길동", year=2024, title="Python 입문") # 순서 바뀌어도 OK!
book_info(year=2024, title="Python 입문", author="홍길동") # 이것도 OK!
|
출력 (모두 동일):
1
2
3
| 제목: Python 입문
저자: 홍길동
출판년도: 2024
|
🎯 학습 목표 3: 키워드 인자의 장점 활용하기
장점 1: 가독성 향상
1
2
3
4
5
6
7
8
9
10
11
12
| # ❌ 위치 인자 - 무엇이 무엇인지 불명확
create_user("john", "john@example.com", "1234", True, False, 25)
# ✅ 키워드 인자 - 명확하고 읽기 쉬움
create_user(
username="john",
email="john@example.com",
password="1234",
is_active=True,
is_admin=False,
age=25
)
|
장점 2: 순서 걱정 없음
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| def send_email(to, subject, body, cc=None, bcc=None):
"""이메일을 보내는 함수"""
print(f"To: {to}")
print(f"Subject: {subject}")
print(f"Body: {body}")
if cc:
print(f"CC: {cc}")
if bcc:
print(f"BCC: {bcc}")
# 순서를 바꿔도 정확히 전달됨
send_email(
body="안녕하세요!",
subject="회의 안내",
to="team@example.com",
cc="boss@example.com"
)
|
장점 3: 선택적 매개변수 지정
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| def format_text(text, bold=False, italic=False, underline=False, color="black"):
"""텍스트 서식을 지정하는 함수"""
result = text
if bold:
result = f"**{result}**"
if italic:
result = f"*{result}*"
if underline:
result = f"_{result}_"
result = f"[{color}] {result}"
return result
# 필요한 것만 지정
print(format_text("중요한 텍스트", bold=True))
print(format_text("강조 텍스트", italic=True, color="red"))
print(format_text("밑줄 텍스트", underline=True))
|
출력:
1
2
3
| [black] **중요한 텍스트**
[red] *강조 텍스트*
[black] _밑줄 텍스트_
|
🎯 학습 목표 4: 매개변수 순서 규칙 이해하기
기본 규칙
중요: 위치 인자는 항상 키워드 인자보다 앞에 와야 합니다!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| def register(username, email, password, newsletter=False, notifications=True):
print(f"사용자명: {username}")
print(f"이메일: {email}")
print(f"비밀번호: {password}")
print(f"뉴스레터 구독: {newsletter}")
print(f"알림 수신: {notifications}")
# ✅ 올바른 순서: 위치 인자 → 키워드 인자
register("john", "john@example.com", "1234", newsletter=True)
# ✅ 모두 위치 인자
register("john", "john@example.com", "1234", True, False)
# ✅ 모두 키워드 인자
register(username="john", email="john@example.com", password="1234", newsletter=True)
# ❌ 잘못된 순서: 키워드 인자 뒤에 위치 인자 (에러!)
# register("john", email="john@example.com", "1234") # SyntaxError!
|
혼합 사용 예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| def create_order(customer_name, product_name, quantity,
express=False, gift_wrap=False, message=""):
"""주문을 생성하는 함수"""
order = {
"고객": customer_name,
"상품": product_name,
"수량": quantity,
"특급배송": express,
"선물포장": gift_wrap,
"메시지": message
}
return order
# 다양한 호출 방식
order1 = create_order("홍길동", "노트북", 1)
print(order1)
order2 = create_order("김철수", "책", 3, express=True)
print(order2)
order3 = create_order("이영희", "꽃", 1, gift_wrap=True, message="생일 축하해!")
print(order3)
# 위치 인자 일부 + 키워드 인자
order4 = create_order("박민수", "시계", 2, message="감사합니다", express=True)
print(order4)
|
🎯 학습 목표 5: 가변 기본값 함정 피하기
주의 1: 가변 기본값 (리스트, 딕셔너리)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| # ❌ 위험! 가변 기본값
def add_item(item, items=[]): # 함수 정의 시 한 번만 생성됨
items.append(item)
return items
# 문제 발생
list1 = add_item("사과")
print(list1) # ['사과']
list2 = add_item("바나나")
print(list2) # ['사과', '바나나'] ⚠️ 이전 값이 남아있음!
list3 = add_item("포도")
print(list3) # ['사과', '바나나', '포도'] ⚠️ 계속 누적됨!
# ✅ 올바른 방법: None을 기본값으로 사용
def add_item_safe(item, items=None):
if items is None:
items = [] # 매번 새로운 리스트 생성
items.append(item)
return items
# 정상 작동
list1 = add_item_safe("사과")
print(list1) # ['사과']
list2 = add_item_safe("바나나")
print(list2) # ['바나나'] ✅ 새로운 리스트!
list3 = add_item_safe("포도")
print(list3) # ['포도'] ✅ 새로운 리스트!
|
주의 2: 기본값은 함수 정의 시 평가됨
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| import datetime
# ❌ 잘못된 방법
def log_message(message, timestamp=datetime.datetime.now()):
"""로그 메시지 (잘못된 예)"""
print(f"[{timestamp}] {message}")
# 모두 같은 시간이 출력됨 (함수 정의 시점의 시간)
log_message("첫 번째 로그")
import time
time.sleep(2)
log_message("두 번째 로그") # 시간이 같음!
# ✅ 올바른 방법
def log_message_correct(message, timestamp=None):
"""로그 메시지 (올바른 예)"""
if timestamp is None:
timestamp = datetime.datetime.now() # 호출 시마다 새로운 시간
print(f"[{timestamp}] {message}")
# 각각 다른 시간이 출력됨
log_message_correct("첫 번째 로그")
time.sleep(2)
log_message_correct("두 번째 로그") # 시간이 다름!
|
주의 3: 키워드 인자 순서
1
2
3
4
5
6
7
8
9
10
11
12
| # ✅ 올바른 순서
def func(a, b, c=0, d=0):
return a + b + c + d
# ✅ 정상 호출
func(1, 2) # 위치 인자만
func(1, 2, 3) # 위치 인자 + 기본값 일부 변경
func(1, 2, c=3, d=4) # 위치 인자 + 키워드 인자
func(1, 2, d=4) # 일부 기본값만 변경
# ❌ 잘못된 순서
# func(1, c=3, 2) # SyntaxError: 키워드 인자 뒤에 위치 인자
|
🎯 학습 목표 6: 실전 함수 설계 패턴 익히기
예제 1: 데이터베이스 연결 함수
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| def connect_database(host="localhost", port=5432, database="mydb",
username="admin", password="", timeout=30):
"""데이터베이스 연결 함수"""
config = {
"호스트": host,
"포트": port,
"데이터베이스": database,
"사용자명": username,
"비밀번호": "***" if password else "(없음)",
"타임아웃": timeout
}
print("=== 데이터베이스 연결 시도 ===")
for key, value in config.items():
print(f"{key}: {value}")
return "연결 성공!"
# 기본 설정으로 연결
result1 = connect_database()
# 일부 설정만 변경
result2 = connect_database(database="production", password="secret123")
# 원격 서버 연결
result3 = connect_database(
host="192.168.1.100",
port=3306,
database="production",
username="root",
password="secure_pass",
timeout=60
)
|
예제 2: HTTP 요청 함수
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| def make_request(url, method="GET", headers=None, data=None, timeout=10):
"""HTTP 요청을 생성하는 함수 (시뮬레이션)"""
print(f"=== HTTP 요청 ===")
print(f"URL: {url}")
print(f"메서드: {method}")
print(f"타임아웃: {timeout}초")
if headers:
print("헤더:")
for key, value in headers.items():
print(f" {key}: {value}")
if data:
print(f"데이터: {data}")
return "요청 성공"
# GET 요청 (기본값 사용)
make_request("https://api.example.com/users")
# POST 요청 with 데이터
make_request(
"https://api.example.com/users",
method="POST",
headers={"Content-Type": "application/json"},
data={"name": "홍길동", "email": "hong@example.com"}
)
# 타임아웃 설정
make_request("https://slow-api.example.com", timeout=60)
|
예제 3: 이미지 처리 함수
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| def process_image(filename, width=None, height=None,
format="png", quality=90, grayscale=False):
"""이미지를 처리하는 함수 (시뮬레이션)"""
print(f"=== 이미지 처리: {filename} ===")
if width or height:
print(f"크기 조정: {width or '자동'}x{height or '자동'}")
print(f"포맷: {format}")
print(f"품질: {quality}%")
if grayscale:
print("흑백 변환 적용")
return f"{filename}.{format}"
# 기본 설정으로 처리
process_image("photo.jpg")
# 크기 조정
process_image("photo.jpg", width=800, height=600)
# 품질과 포맷 변경
process_image("photo.jpg", format="jpg", quality=85)
# 흑백 변환
process_image("photo.jpg", width=400, grayscale=True)
|
💡 실전 팁 & 주의사항
💡 Tip 1: 기본값으로 자주 쓰는 값 설정하기
함수를 사용할 때 가장 자주 쓰는 값을 기본값으로 설정하세요.
1
2
3
4
5
6
7
8
9
10
| # ✅ 좋은 예시
def send_email(to, subject, body, priority="normal"):
"""대부분의 이메일은 보통 우선순위"""
pass
# 대부분의 경우
send_email("user@example.com", "회의 안내", "내일 10시 회의")
# 긴급한 경우만 명시
send_email("boss@example.com", "긴급", "확인 요망", priority="high")
|
💡 Tip 2: 키워드 인자로 가독성 높이기
매개변수가 3개 이상이면 키워드 인자를 사용하세요.
1
2
3
4
5
6
7
8
9
10
11
12
| # ❌ 읽기 어려움
create_user("john", "john@example.com", "1234", True, False, 25)
# ✅ 명확하고 읽기 쉬움
create_user(
username="john",
email="john@example.com",
password="1234",
is_active=True,
is_admin=False,
age=25
)
|
💡 Tip 3: 선택적 매개변수는 뒤에 배치
필수 매개변수는 앞에, 선택적 매개변수는 뒤에 배치하세요.
1
2
3
4
5
6
| # ✅ 올바른 순서
def register(username, email, password, newsletter=False):
pass
# ❌ 잘못된 순서
# def register(newsletter=False, username, email, password): # SyntaxError!
|
💡 Tip 4: 가변 기본값은 절대 사용 금지! ⚠️
리스트, 딕셔너리 등 가변 객체는 기본값으로 사용하지 마세요.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # ❌ 위험! 누적됨
def add_item(item, items=[]):
items.append(item)
return items
list1 = add_item("사과") # ['사과']
list2 = add_item("바나나") # ['사과', '바나나'] ⚠️ 누적!
# ✅ 안전: None 사용
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
|
💡 Tip 5: 시간 관련 기본값도 주의 ⚠️
datetime 같은 시간 관련 기본값도 None을 사용하세요.
1
2
3
4
5
6
7
8
9
| # ❌ 모두 같은 시간 (함수 정의 시점의 시간)
def log(message, timestamp=datetime.now()):
pass
# ✅ 호출 시마다 다른 시간
def log(message, timestamp=None):
if timestamp is None:
timestamp = datetime.now()
pass
|
💡 Tip 6: 위치 인자 먼저, 키워드 인자 나중 ⚠️
1
2
3
4
5
| # ✅ 올바른 순서
func(1, 2, c=3, d=4)
# ❌ 잘못된 순서
# func(1, c=3, 2) # SyntaxError!
|
💡 Tip 7: 필수 매개변수를 기본값 뒤에 두지 않기 ⚠️
1
2
3
4
5
6
7
| # ❌ SyntaxError!
# def func(a=1, b, c):
# pass
# ✅ 필수 매개변수 먼저
def func(b, c, a=1):
pass
|
💡 Tip 8: 설정 함수 패턴 활용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| def configure_server(port=8000, host="0.0.0.0", debug=False,
workers=4, max_connections=100):
"""서버 설정 함수"""
config = {
"포트": port,
"호스트": host,
"디버그 모드": debug,
"워커 수": workers,
"최대 연결": max_connections
}
print("=== 서버 설정 ===")
for key, value in config.items():
print(f"{key}: {value}")
return config
# 개발 환경 (디버그 모드)
dev_config = configure_server(debug=True, workers=1)
# 운영 환경 (포트 변경, 워커 증가)
prod_config = configure_server(port=80, workers=8, max_connections=1000)
# 테스트 환경 (다른 포트)
test_config = configure_server(port=9000)
|
💡 Tip 9: 플러그인 시스템 패턴 - 선택적 기능 활성화
여러 옵션을 조합할 수 있는 유연한 함수를 만들 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| def apply_filter(image, blur=0, brightness=0, contrast=0, sharpen=False):
"""이미지 필터 적용 - 원하는 필터만 선택"""
filters = []
if blur > 0:
filters.append(f"블러: {blur}%")
if brightness != 0:
filters.append(f"밝기: {brightness:+d}%")
if sharpen:
filters.append("샤프닝 적용")
print(f"'{image}'에 적용: {', '.join(filters) if filters else '없음'}")
return image
# 필요한 필터만 활성화
apply_filter("photo.jpg", blur=5)
apply_filter("photo.jpg", brightness=20, contrast=10)
|
💡 Tip 10: API 래퍼 패턴 - 복잡한 기본 설정
복잡한 함수는 합리적인 기본값으로 간단하게 사용하고, 필요시 세밀하게 제어할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
| def api_call(endpoint, method="GET", params=None,
retry=3, timeout=30, verify_ssl=True):
"""API 호출 - 대부분은 기본값으로 충분"""
print(f"{method} {endpoint} (재시도:{retry}, 타임아웃:{timeout}초)")
return {"status": 200}
# 간단한 사용
api_call("/api/users")
# 필요시 세밀한 제어
api_call("/api/users", method="POST", retry=5, timeout=60)
|
💡 Tip 11: Boolean 플래그는 명확하게 이름 짓기
Boolean 매개변수는 is_, has_, should_ 등으로 시작하면 가독성이 좋습니다.
1
2
3
4
5
6
7
8
9
| # ✅ 명확한 이름
def send_email(to, subject, body, should_retry=True, is_urgent=False):
pass
send_email("user@example.com", "안내", "내용", is_urgent=True)
# ❌ 불명확한 이름
def send_email(to, subject, body, retry=True, urgent=False):
pass
|
💡 Tip 12: None과 빈 값 구분하기
None을 기본값으로 사용하면 “값이 전달되지 않음”과 “빈 값이 전달됨”을 구분할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
| def search(query, filters=None, limit=10):
"""검색 함수"""
if filters is None:
filters = [] # 기본: 빈 필터
print(f"검색어: {query}, 필터: {filters}, 제한: {limit}")
search("Python") # 필터: []
search("Python", filters=[]) # 필터: []
search("Python", filters=["recent"]) # 필터: ['recent']
|
💡 Tip 13: 문서화에 기본값 명시하기
독스트링에 매개변수 기본값을 명시하면 사용자가 이해하기 쉽습니다.
1
2
3
4
5
6
7
8
9
10
11
| def create_user(username, email, role="user", is_active=True):
"""
사용자를 생성합니다.
Args:
username (str): 사용자명 (필수)
email (str): 이메일 (필수)
role (str): 권한 역할 (기본값: "user")
is_active (bool): 활성화 여부 (기본값: True)
"""
pass
|
💡 Tip 14: 환경별 설정 분리하기
개발/테스트/운영 환경별로 다른 기본값을 제공할 수 있습니다.
1
2
3
4
5
6
7
8
9
| def connect_db(host="localhost", port=5432, pool_size=5, debug=False):
"""데이터베이스 연결"""
pass
# 개발 환경
connect_db(debug=True)
# 운영 환경
connect_db(host="prod-db.example.com", pool_size=20)
|
💡 Tip 15: 너무 많은 매개변수는 딕셔너리로 묶기
매개변수가 5개 이상이면 관련된 것끼리 딕셔너리로 묶는 것을 고려하세요.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| # ❌ 너무 많은 매개변수
def create_server(host, port, workers, timeout, max_conn, debug, ssl):
pass
# ✅ 관련 설정을 딕셔너리로
def create_server(host, port, config=None):
"""
서버 생성
config 예시: {
'workers': 4,
'timeout': 30,
'max_connections': 100,
'debug': False,
'use_ssl': True
}
"""
if config is None:
config = {'workers': 4, 'timeout': 30}
pass
|
🧪 연습 문제
문제 1: 프로필 카드 생성기
다음 요구사항을 만족하는 create_profile_card 함수를 작성하세요.
- 필수: name
- 선택: title (기본값: “사원”), department (기본값: “일반”), email (기본값: None)
- 이메일이 있으면 함께 출력, 없으면 생략
💡 힌트
기본 매개변수를 사용하고, 조건문으로 이메일 유무를 확인하세요.
1
2
3
4
5
| def create_profile_card(name, title="사원", ...):
if email:
# 이메일 포함 출력
else:
# 이메일 없이 출력
|
✅ 정답
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| def create_profile_card(name, title="사원", department="일반", email=None):
"""프로필 카드를 생성하는 함수"""
print("=" * 40)
print(f"이름: {name}")
print(f"직급: {title}")
print(f"부서: {department}")
if email:
print(f"이메일: {email}")
print("=" * 40)
# 테스트
create_profile_card("홍길동")
create_profile_card("김철수", "대리", "개발팀")
create_profile_card("이영희", "과장", "마케팅팀", "lee@example.com")
create_profile_card("박민수", department="인사팀", email="park@example.com")
|
출력:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| ========================================
이름: 홍길동
직급: 사원
부서: 일반
========================================
========================================
이름: 김철수
직급: 대리
부서: 개발팀
========================================
========================================
이름: 이영희
직급: 과장
부서: 마케팅팀
이메일: lee@example.com
========================================
========================================
이름: 박민수
직급: 사원
부서: 인사팀
이메일: park@example.com
========================================
|
문제 2: 할인 가격 계산기
다음 요구사항을 만족하는 calculate_price 함수를 작성하세요.
- 필수: price (원가)
- 선택: discount (할인율 %, 기본값: 0), tax (세금 %, 기본값: 10), delivery_fee (배송비, 기본값: 3000)
- 계산 순서: 할인 적용 → 세금 추가 → 배송비 추가
- 소수점 이하 반올림
💡 힌트
순서대로 계산하세요:
- 할인 가격 = price * (1 - discount/100)
- 세금 포함 = 할인 가격 * (1 + tax/100)
- 최종 가격 = 세금 포함 + 배송비
round() 함수를 사용하세요.
✅ 정답
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| def calculate_price(price, discount=0, tax=10, delivery_fee=3000):
"""최종 가격을 계산하는 함수"""
# 할인 적용
discounted = price * (1 - discount / 100)
# 세금 추가
with_tax = discounted * (1 + tax / 100)
# 배송비 추가
final_price = with_tax + delivery_fee
# 결과 출력
print(f"원가: {price:,}원")
if discount > 0:
print(f"할인({discount}%): -{price - discounted:,.0f}원")
print(f"세금({tax}%): +{with_tax - discounted:,.0f}원")
print(f"배송비: +{delivery_fee:,}원")
print(f"최종 가격: {round(final_price):,}원")
print()
return round(final_price)
# 테스트
calculate_price(50000)
calculate_price(50000, discount=20)
calculate_price(50000, discount=20, tax=0)
calculate_price(50000, discount=15, tax=10, delivery_fee=0)
|
출력:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| 원가: 50,000원
세금(10%): +5,000원
배송비: +3,000원
최종 가격: 58,000원
원가: 50,000원
할인(20%): -10,000원
세금(10%): +4,000원
배송비: +3,000원
최종 가격: 47,000원
원가: 50,000원
할인(20%): -10,000원
세금(0%): +0원
배송비: +3,000원
최종 가격: 43,000원
원가: 50,000원
할인(15%): -7,500원
세금(10%): +4,250원
배송비: +0원
최종 가격: 46,750원
|
문제 3: 로그 메시지 포매터
다음 요구사항을 만족하는 log 함수를 작성하세요.
- 필수: message (로그 메시지)
- 선택: level (로그 레벨, 기본값: “INFO”), timestamp (시간 표시 여부, 기본값: True), prefix (접두사, 기본값: “”)
- 레벨에 따라 다른 형식으로 출력
- timestamp가 True면 현재 시간 포함
💡 힌트
datetime 모듈을 사용하세요:
1
2
3
| from datetime import datetime
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
레벨별 이모지:
- DEBUG: 🐛
- INFO: ℹ️
- WARNING: ⚠️
- ERROR: ❌
✅ 정답
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
| from datetime import datetime
def log(message, level="INFO", timestamp=True, prefix=""):
"""로그 메시지를 출력하는 함수"""
# 레벨별 이모지
level_emoji = {
"DEBUG": "🐛",
"INFO": "ℹ️",
"WARNING": "⚠️",
"ERROR": "❌"
}
# 로그 구성
parts = []
# 시간 추가
if timestamp:
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
parts.append(f"[{now}]")
# 레벨 추가
emoji = level_emoji.get(level, "📝")
parts.append(f"{emoji} [{level}]")
# 접두사 추가
if prefix:
parts.append(f"[{prefix}]")
# 메시지 추가
parts.append(message)
# 출력
print(" ".join(parts))
# 테스트
log("프로그램 시작")
log("데이터베이스 연결 성공", level="DEBUG")
log("디스크 공간 부족", level="WARNING")
log("파일을 찾을 수 없음", level="ERROR", prefix="FileSystem")
log("사용자 로그인", timestamp=False, prefix="Auth")
|
출력:
1
2
3
4
5
| [2024-03-26 10:30:45] ℹ️ [INFO] 프로그램 시작
[2024-03-26 10:30:45] 🐛 [DEBUG] 데이터베이스 연결 성공
[2024-03-26 10:30:45] ⚠️ [WARNING] 디스크 공간 부족
[2024-03-26 10:30:45] ❌ [ERROR] [FileSystem] 파일을 찾을 수 없음
ℹ️ [INFO] [Auth] 사용자 로그인
|
💬 자주 묻는 질문 (FAQ)
Q1. 기본 매개변수와 키워드 인자의 차이는 무엇인가요?
A: 기본 매개변수는 함수를 정의할 때 매개변수에 기본값을 설정하는 것이고, 키워드 인자는 함수를 호출할 때 매개변수 이름을 명시하여 값을 전달하는 방식입니다.
1
2
3
4
5
6
| # 기본 매개변수 (함수 정의)
def greet(name="손님"):
print(f"안녕하세요, {name}님!")
# 키워드 인자 (함수 호출)
greet(name="철수")
|
Q2. 왜 리스트나 딕셔너리를 기본값으로 사용하면 안 되나요?
A: 기본값은 함수 정의 시 단 한 번만 생성되어 모든 호출에서 같은 객체를 공유하기 때문입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # ❌ 위험!
def add_item(item, items=[]):
items.append(item)
return items
list1 = add_item("사과") # ['사과']
list2 = add_item("바나나") # ['사과', '바나나'] ⚠️ 누적!
# list1과 list2는 같은 리스트 객체를 가리킴!
# ✅ 안전
def add_item(item, items=None):
if items is None:
items = [] # 매번 새로운 리스트 생성
items.append(item)
return items
|
Q3. 위치 인자와 키워드 인자를 섞어 쓸 수 있나요?
A: 네, 가능합니다! 단, 위치 인자는 반드시 키워드 인자보다 앞에 와야 합니다.
1
2
3
4
5
6
7
8
| def register(username, email, password, newsletter=False):
pass
# ✅ 올바른 순서
register("john", "john@example.com", "1234", newsletter=True)
# ❌ 잘못된 순서
# register("john", email="john@example.com", "1234") # SyntaxError!
|
Q4. 모든 매개변수를 키워드 인자로 전달할 수 있나요?
A: 네! 모든 매개변수를 키워드 인자로 전달하면 순서에 상관없이 명확하게 값을 전달할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
| def book_flight(from_city, to_city, date, passengers=1):
pass
# 순서 바꿔도 OK!
book_flight(
date="2024-04-01",
to_city="부산",
from_city="서울",
passengers=2
)
|
Q5. print()의 end=”\n”도 기본 매개변수인가요?
A: 정확합니다! print() 함수도 여러 기본 매개변수를 가지고 있습니다.
1
2
3
4
5
6
7
| # print 함수의 실제 정의 (간략화)
def print(*values, sep=' ', end='\n', file=sys.stdout):
pass
# 그래서 이렇게 변경 가능
print("안녕", "세상", sep="-", end="!\n")
# 출력: 안녕-세상!
|
Q6. None을 기본값으로 쓰는 패턴이 왜 안전한가요?
A: None은 불변(immutable) 객체이고, 함수 내부에서 새로운 객체를 생성하기 때문에 안전합니다.
1
2
3
4
5
6
7
8
9
10
| def process_data(data=None):
if data is None:
data = [] # 매번 새로운 리스트 생성
data.append("item")
return data
# 각 호출마다 독립적인 리스트
result1 = process_data() # ['item']
result2 = process_data() # ['item'] (새로운 리스트)
|
Q7. 매개변수가 너무 많으면 어떻게 해야 하나요?
A: 매개변수가 5개 이상이면 관련된 것끼리 딕셔너리로 묶거나, 클래스를 사용하는 것을 고려하세요.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # 방법 1: 딕셔너리로 묶기
def create_server(host, port, config=None):
if config is None:
config = {'workers': 4, 'timeout': 30}
pass
# 방법 2: 데이터 클래스 (고급)
from dataclasses import dataclass
@dataclass
class ServerConfig:
workers: int = 4
timeout: int = 30
debug: bool = False
def create_server(host, port, config=ServerConfig()):
pass
|
Q8. 함수를 호출할 때 매번 모든 매개변수 이름을 써야 하나요?
A: 아니요! 필요한 것만 키워드 인자로 지정하면 됩니다. 보통 3개 이상일 때 가독성을 위해 사용합니다.
1
2
3
4
5
6
7
8
| def send_email(to, subject, body, cc=None, bcc=None, priority="normal"):
pass
# 필수만 위치 인자로
send_email("user@example.com", "안내", "내용")
# 필요한 것만 키워드 인자로 추가
send_email("user@example.com", "긴급", "내용", priority="high")
|
📝 오늘 배운 내용 정리
- 기본 매개변수: 함수 정의 시 기본값 설정 (
def func(param=기본값)) - 키워드 인자: 매개변수 이름을 명시해서 값 전달 (
func(param=값)) - 혼합 사용: 위치 인자 먼저, 키워드 인자 나중
- 장점: 유연성, 가독성, 선택적 매개변수
- 주의: 가변 기본값은 None 사용, 순서 규칙 준수
🎯 실습 과제
과제: 블로그 포스트 생성기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
| def create_blog_post(title, content, author="익명",
category="일반", tags=None, published=True):
"""블로그 포스트를 생성하는 함수"""
from datetime import datetime
# 태그 처리
if tags is None:
tags = []
# 포스트 정보
post = {
"제목": title,
"내용": content,
"작성자": author,
"카테고리": category,
"태그": tags,
"발행 상태": "발행됨" if published else "임시저장",
"작성일": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
# 포스트 출력
print("=" * 60)
print(f"제목: {post['제목']}")
print(f"작성자: {post['작성자']}")
print(f"카테고리: {post['카테고리']}")
if post['태그']:
print(f"태그: {', '.join(post['태그'])}")
print(f"상태: {post['발행 상태']}")
print(f"작성일: {post['작성일']}")
print("-" * 60)
print(post['내용'])
print("=" * 60)
return post
# 다양한 방식으로 포스트 생성
post1 = create_blog_post(
"Python 기초",
"Python은 배우기 쉬운 프로그래밍 언어입니다."
)
post2 = create_blog_post(
"함수 활용법",
"함수를 사용하면 코드를 재사용할 수 있습니다.",
author="홍길동",
category="프로그래밍",
tags=["Python", "함수", "기초"]
)
post3 = create_blog_post(
"작업 중인 글",
"아직 완성되지 않은 글입니다.",
published=False
)
post4 = create_blog_post(
content="짧은 메모입니다.",
title="메모",
tags=["메모"],
category="일기"
)
|
🔗 관련 자료
📚 이전 학습
Day 25: 함수 매개변수와 반환값 ⭐⭐⭐
어제는 위치 인자, 키워드 인자, return으로 값을 반환하는 방법을 배웠습니다!
📚 다음 학습
Day 27: 가변 인자 (*args, **kwargs) ⭐⭐⭐
내일은 개수가 정해지지 않은 인자를 받는 가변 인자를 배웁니다!
“늦었다고 생각할 때가 가장 빠른 시기입니다!” 🚀
| Day 26/100 | Phase 3: 제어문과 함수 | #100DaysOfPython |