파이썬 함수의 모든 매개변수 종류와 가변 객체 기본값 함정을 완전히 이해한다.
🎯 이 강의에서 배우는 것
파이썬의 매개변수 시스템은 매우 정교합니다. 위치·키워드·가변·기본값·위치 전용·키워드 전용 인수까지 6가지 종류가 있습니다. 그 중 가변 객체 기본값의 함정은 파이썬 개발자 모두가 한 번은 맞닥뜨리는 유명한 버그입니다. 이 강의에서 모든 패턴을 완벽하게 이해합니다.
📌 위치 인자와 키워드 인자
def describe_person(name, age, city):
print(f"{name}님은 {city}에 사는 {age}세입니다.")
# 위치 인자: 순서 중요
describe_person("홍길동", 30, "서울")
# 키워드 인자: 순서 무관, 이름으로 전달
describe_person(city="부산", name="이순신", age=45)
# 혼합: 위치 인자가 항상 먼저
describe_person("강감찬", city="대전", age=55)
# 에러: 키워드 인자 뒤에 위치 인자
# describe_person("강감찬", age=55, "대전") ← SyntaxError
# 중복 전달 에러
# describe_person("홍길동", 30, "서울", name="홍길동") ← TypeError
⚠️ 기본값의 함정 — 가변 객체 기본값
# 유명한 파이썬 버그: 가변 객체를 기본값으로 사용
def add_item_buggy(item, lst=[]): # 리스트는 def 실행 시 한 번만 생성됨!
lst.append(item)
return lst
print(add_item_buggy("a")) # ['a'] ← 기대: ['a']
print(add_item_buggy("b")) # ['a', 'b'] ← 기대: ['b'] — 버그!
print(add_item_buggy("c")) # ['a', 'b', 'c'] ← 누적됨!
# 이유: 기본값은 함수 정의 시점에 단 한 번 평가됨
# add_item_buggy.__defaults__ → (['a', 'b', 'c'],) ← 공유되는 객체!
# ✅ 올바른 패턴: None을 기본값으로, 함수 본문에서 생성
def add_item(item, lst=None):
if lst is None:
lst = [] # 호출마다 새 리스트 생성
lst.append(item)
return lst
print(add_item("a")) # ['a']
print(add_item("b")) # ['b'] ← 올바른 동작
print(add_item("c", [1, 2])) # [1, 2, 'c']
# 불변 객체 기본값은 안전
def greet(name, greeting="안녕하세요"): # 문자열은 불변 → 안전
return f"{greeting}, {name}님!"
📦 *args — 가변 위치 인자
def my_sum(*args):
# args는 추가 위치 인자를 담은 튜플
print(type(args), args)
return sum(args)
print(my_sum(1, 2, 3)) # <class 'tuple'> (1, 2, 3) → 6
print(my_sum(10, 20)) # (10, 20) → 30
print(my_sum()) # () → 0
# 앞에 일반 매개변수 혼합 가능
def make_tag(tag, *contents):
inner = "".join(str(c) for c in contents)
return f"<{tag}>{inner}</{tag}>"
print(make_tag("p", "안녕하세요"))
print(make_tag("ul", "<li>사과</li>", "<li>바나나</li>"))
# * 언패킹으로 이터러블을 위치 인자로 전달
nums = [1, 2, 3, 4, 5]
print(my_sum(*nums)) # my_sum(1, 2, 3, 4, 5)와 동일
# 여러 이터러블 병합
a = [1, 2, 3]
b = [4, 5, 6]
combined = [*a, *b]
print(combined) # [1, 2, 3, 4, 5, 6]
📖 **kwargs — 가변 키워드 인자
def create_html_tag(tag, **attrs):
# attrs는 키워드 인자를 담은 딕셔너리
attr_str = " ".join(f'{k}="{v}"' for k, v in attrs.items())
return f"<{tag} {attr_str}></{tag}>" if attr_str else f"<{tag}></{tag}>"
print(create_html_tag("a", href="https://example.com", target="_blank"))
print(create_html_tag("div", id="main", class_="container"))
print(create_html_tag("br")) # attrs 없음
# ** 언패킹으로 딕셔너리를 키워드 인자로 전달
config = {"href": "https://example.com", "target": "_blank"}
print(create_html_tag("a", **config))
# 두 딕셔너리 병합 (Python 3.9+: | 연산자)
defaults = {"color": "blue", "size": 12}
overrides = {"size": 16, "weight": "bold"}
merged = {**defaults, **overrides} # overrides가 기존 값 덮어씀
print(merged) # {'color': 'blue', 'size': 16, 'weight': 'bold'}
# *args + **kwargs 조합: 모든 인자 받기
def log_call(*args, **kwargs):
print(f"인자: {args}")
print(f"키워드: {kwargs}")
log_call(1, 2, 3, name="홍길동", age=30)
🔒 위치 전용(/) vs 키워드 전용(*) 매개변수
# Python 3.8+ 구분자
# / 왼쪽: 위치 전용 (키워드로 전달 불가)
# * 오른쪽: 키워드 전용 (위치로 전달 불가)
def func(pos_only, /, normal, *, kw_only):
print(f"pos_only={pos_only}, normal={normal}, kw_only={kw_only}")
func(1, 2, kw_only=3) # ✅
func(1, normal=2, kw_only=3) # ✅ normal은 키워드 가능
# func(pos_only=1, normal=2, kw_only=3) ← TypeError: pos_only는 키워드 불가
# func(1, 2, 3) ← TypeError: kw_only는 위치 불가
# 내장 함수가 위치 전용을 많이 씀
help(len) # len(obj, /) ← obj는 위치 전용
# len(obj=["a", "b"]) ← TypeError
# 완전한 매개변수 순서
def full_example(a, b, /, c, d, *args, e, f, **kwargs):
pass
# a, b → 위치 전용
# c, d → 위치 또는 키워드
# *args → 가변 위치
# e, f → 키워드 전용
# **kwargs → 가변 키워드
🔗 인자 전달 방식 — Call by Object Reference
# 파이썬의 인자 전달: call by sharing (call by object reference)
# 함수는 객체에 대한 참조를 받음
def test_immutable(x):
print(f"함수 안 id: {id(x)}")
x = x + 1 # 새 int 객체 생성, 지역 변수 x가 새 객체를 가리킴
print(f"재할당 후 id: {id(x)}")
n = 10
print(f"함수 밖 id: {id(n)}")
test_immutable(n)
print(f"함수 호출 후 n: {n}") # 10 — 변하지 않음!
# int는 불변 → 함수 안에서 재할당해도 외부 변수에 영향 없음
def test_mutable(lst):
print(f"함수 안 id: {id(lst)}")
lst.append(99) # 같은 객체를 수정!
my_list = [1, 2, 3]
print(f"함수 밖 id: {id(my_list)}")
test_mutable(my_list)
print(f"함수 호출 후 my_list: {my_list}") # [1, 2, 3, 99] — 변경됨!
# list는 가변 → 함수 안에서 수정하면 외부에 영향
# 가변 객체를 수정하지 않으려면 복사본 전달
test_mutable(my_list.copy()) # 원본 보호
print(f"복사 후 my_list: {my_list}") # [1, 2, 3, 99] — 변화 없음
⚠️ 자주 하는 실수
- 가변 객체 기본값:
def f(lst=[])는 모든 호출이 같은 리스트를 공유합니다. 반드시lst=None으로 선언하고 함수 본문에서 생성하세요. - *args 뒤에 일반 매개변수:
def f(*args, x)에서 x는 반드시 키워드로 전달해야 합니다. 위치로 전달하면 args가 먹어버립니다. - ** 언패킹에서 키 충돌:
f(**d1, **d2)에서 d1과 d2에 같은 키가 있으면 TypeError. 미리 확인하거나 d1.update(d2)로 병합 후 전달하세요.
📝 정리 및 다음 강의 예고
- 기본값은 함수 정의 시점에 한 번만 평가 — 가변 객체(list, dict)는
None으로 선언 후 함수 본문에서 생성. *args: 추가 위치 인자를 튜플로.**kwargs: 추가 키워드 인자를 딕셔너리로./왼쪽은 위치 전용,*오른쪽은 키워드 전용 (Python 3.8+).- 파이썬은 call by object reference — 가변 객체 수정은 외부에 영향, 재할당은 영향 없음.
다음 강의: 반환값과 함수 설계 원칙 — 순수 함수와 부수 효과의 차이, 단일 책임 원칙을 배웁니다.
관련 주제
- 위치 인자·키워드 인자
- 가변 객체 기본값 함정
- *args **kwargs
- 위치전용·키워드전용 매개변수(/ *)
- call by object reference
- 개발·프로그래밍
- 개발·프로그래밍 강의
- 파이썬 기초 40강 — 처음 배우는 프로그래밍
- 무료강의
- 무료 온라인 강의
- NUGUNA
- 누구나
📚 시리즈 전체 공유
파이썬 기초 40강 — 처음 배우는 프로그래밍
이 강의가 속한 시리즈는 총 32강, 모두 무료입니다. 처음부터 배우려는 동료에게 시리즈 전체를 알려 주세요.
댓글
0/1000
불러오는 중...
