파이썬의 비밀스러운 삶: Default 함정
Source: Dev.to
왜 빈 리스트를 기본 인수로 절대 사용하면 안 되는지.
🎧 오디오 버전: 듣는 걸 선호하시나요? 이 심층 탐구의 AI 팟캐스트 확장 버전을 YouTube에서 확인하세요.
📺 비디오 버전: 보는 걸 선호하시나요? 7분짜리 시각적 설명을 YouTube에서 확인하세요.
공유 메모리
def add_student(name, student_list=[]):
student_list.append(name)
return student_list
# First call
print(add_student("Alex"))
# Second call
print(add_student("Alice"))
두 번째 호출이 ['Alex', 'Alice']를 반환하는 이유는 기본 리스트가 함수가 정의될 때 한 번만 생성되고, 호출될 때마다 새로 만들어지지 않기 때문입니다.
“좀비” 변수 보기
__defaults__ 속성을 통해 저장된 기본값을 확인할 수 있습니다:
print(f"Before: {add_student.__defaults__}")
add_student("Alex")
print(f"After: {add_student.__defaults__}")
출력
Before: ([],)
After: (['Alex'],)
리스트가 함수 객체에 붙어 있어 호출 사이에 지속됩니다. 이 동작은 mutable(변경 가능한) 기본값(예: 리스트, 딕셔너리)에서만 발생합니다. None, 문자열, 정수와 같은 immutable(변경 불가능) 기본값은 변경될 수 없으므로 안전합니다.
전문가 표준
권장되는 패턴은 None을 sentinel 값으로 사용하고 함수 내부에서 새 리스트를 만드는 것입니다:
def add_student(name, student_list=None):
if student_list is None:
student_list = []
student_list.append(name)
return student_list
이제 명시적인 리스트가 제공되지 않을 때마다 매 호출마다 새로운 리스트가 생성됩니다.
Margaret’s Cheat Sheet: 기본 인수
- 안전한 기본값:
None,True,False,0,""(불변 객체) - 위험한 기본값:
[],{},set()(가변 객체)
문제점: 기본 인수는 정의 시점에 한 번 평가됩니다.
해결책: 기본값으로 None을 사용하고 함수 본문 안에서 가변 객체를 초기화합니다.
디버거: my_function.__defaults__를 검사하여 숨겨진 공유 상태를 감지합니다.