Python 개발자들이 인터뷰에서 기본 가변 인자(Default Mutable Argument) 질문에 실패하는 이유
요약
Python의 기본 가변 인자(Default Mutable Argument)가 함수 정의 시점에 단 한 번 평가되어 발생하는 동작 원리를 설명합니다. 이로 인해 발생하는 예기치 않은 상태 공유 문제를 다루며, 기술 인터뷰에서 자주 등장하는 변형 문제들을 소개합니다.
핵심 포인트
- 기본 인자 값은 함수 호출 시가 아닌 정의 시점에 평가됨
- 가변 객체(list, dict 등)를 기본값으로 사용하면 상태가 유지됨
- 함수 객체의 __defaults__ 속성을 통해 기본값 확인 가능
- 인터뷰 대비를 위해 딕셔너리나 조건부 로직 변형 패턴 숙지 필요
Python 개발자들이 인터뷰에서 기본 가변 인자(Default Mutable Argument) 질문에 실패하는 이유
다른 어떤 단일 개념보다 더 많은 기술 인터뷰를 끝내버린 Python의 동작 방식이 있습니다. 이는 스타트업의 신입급 스크리닝부터 FAANG 기업의 시니어급 루프에 이르기까지 공통적으로 등장합니다.
그것은 바로 기본 가변 인자 함정 (default mutable argument trap) 입니다.
가장 전형적인 버전은 다음과 같습니다:
def add_item(item, items=[]):
items.append(item)
return items
...
더 읽기 전에, 이것이 무엇을 출력할지 적어보세요.
대부분의 사람들은 다음과 같이 적습니다:
['a']
['b']
['c']
...
실제 출력값은 다음과 같습니다:
['a']
['a', 'b']
['a', 'b', 'c']
...
만약 틀렸다면, 당신과 같은 실수를 하는 사람이 많으니 안심하세요. 만약 맞혔다면, 이 글은 왜 이런 방식으로 작동하는지, 그리고 면접관들이 기본 버전을 아는 사람들을 골탕 먹이기 위해 어떤 변형을 사용하는지 설명합니다.
왜 이런 일이 발생하는가
Python에서 기본 인자 값(default argument values)은 함수가 호출될 때마다 평가되는 것이 아니라, 함수가 정의될 때 단 한 번 평가됩니다.
이 한 문장이 모든 설명의 전부입니다. def add_item(item, items=[])에서 리스트 []는 Python이 함수 정의를 파싱할 때 단 한 번 생성됩니다. 이는 함수 객체 자체의 일부로 저장됩니다. 명시적인 items 인자를 전달하지 않는 모든 add_item 호출은 동일한 리스트 객체를 공유합니다.
함수의 내부 속성을 확인하여 이를 직접 확인할 수 있습니다:
print(add_item.__defaults__)
# 세 번의 호출 후, 결과는 다음과 같습니다: (['a', 'b', 'c'],)
기본값은 함수 내부에 존재하며 호출될 때마다 변합니다(mutate).
이 문제에 대한 추적 테이블 (Trace Table)
추적 테이블(Trace table)은 실행에 따라 상태가 어떻게 변하는지 시각화하는 데 도움이 됩니다.
| 호출 (Call) | item | items (기본 객체) | append 후 | 반환값 (Returns) |
|---|---|---|---|---|
add_item("a") | "a" | [] | ["a"] | ["a"] |
| ... |
핵심은 세 번째 열입니다. 기본 객체는 호출 사이에 초기화되지 않습니다. 상태를 계속 유지하며 다음으로 전달합니다.
면접관들이 문제를 더 어렵게 만드는 방법
기본적인 함정을 파악했다면, 면접관들은 여기에 층을 더 쌓습니다. 가장 흔한 세 가지 변형은 다음과 같습니다:
변형 1: 딕셔너리 기본값 (The dictionary default)
동일한 동작을 보이지만, 데이터 구조만 다릅니다.
def update_config(key, value, config={}):
config[key] = value
return config
...
출력 결과:
{'debug': True}
{'debug': True, 'verbose': False}
변형 2: 조건부 쓰기 (The conditional write)
두 번째 호출에서는 조건부 체크 때문에 중복되지 않지만, 근본적인 리스트 (list)는 여전히 유지됩니다.
def add_if_missing(item, items=[]):
if item not in items:
items.append(item)
...
출력 결과:
['x']
['x']
['x', 'y']
...
변형 3: None 패턴 수정 (The None pattern fix)
면접관들은 때때로 올바른 버전을 보여주고, 그것이 정확히 왜 작동하는지 설명하라고 요구합니다:
def add_item_fixed(item, items=None):
if items is None:
items = []
...
여기서 None은 불변 (immutable)입니다. items가 제공되지 않는 매 호출마다 함수 본문 내부에서 새롭고 깨끗한 리스트가 생성됩니다. 기본 참조 (default reference) 자체는 절대 변하지 않습니다.
면접관이 실제로 테스트하는 것
면접관이 기본 가변 인자 (default mutable argument) 질문을 던질 때, 그들은 당신이 이 특정 기이한 현상을 암기했는지 테스트하는 것이 아닙니다. 그들은 더 깊은 세 가지 요소를 테스트하고 있습니다:
-
인터프리터 메커니즘 (Interpreter Mechanics): Python이 기본값을 호출 시점 (call time)이 아닌 정의 시점 (definition time)에 평가한다는 것을 이해하고 있는지 확인합니다. 이를 위해서는 Python 인터프리터가 함수 객체를 어떻게 파싱하고 저장하는지에 대한 이해가 필요합니다.
-
상태 추적 (State Tracking): 코드를 실행하지 않고도 수동으로 실행 과정을 추적할 수 있는지 확인합니다. 상태에 대해 추론하여 출력을 예측하는 능력은 디버깅 (debugging), 코드 리뷰 (code review), 시스템 설계 (system design)로 이어지는 근본적인 기술입니다.
-
모범 사례 (Best Practices): 버그를 피하기 위한 올바른 패턴을 알고 있는지 확인합니다.
None센티널 (sentinel) 패턴은 업계에서 통용되는 해결책입니다. 이 패턴의 존재를 알고 그것이 왜 작동하는지 설명할 수 있다는 것은, 단순히 함정이 있다는 것을 아는 수준을 넘어선 깊이를 보여줍니다.
이러한 유형의 문제를 연습하는 방법
가변 기본 인자 (Mutable Default Argument) 질문 및 이와 유사한 Python 함정들을 대비하기 위한 가장 효과적인 연습 방법은 드라이 런 트레이싱 (Dry-run tracing)입니다. 코드를 한 줄씩 읽으며, 표를 만들어 모든 변수의 상태를 추적하고, 코드를 실행하기 전에 출력을 예측하는 방식입니다.
저는 바로 이러한 범주의 문제들에 대해 고유한 Python 트레이싱 문제를 생성해 주는 PyCodeIt라는 무료 도구를 만들었습니다. 각 문제는 AI로 생성되므로 똑같은 질문을 두 번 볼 일이 없으며, 각 정답 뒤에 제공되는 설명은 전체 변수 추적 (Variable trace) 과정을 보여줍니다.
계정을 생성하지 않고도 pycodeit.com에서 바로 체험해 보실 수 있습니다.
공부해야 할 세 가지 추가 가변 함정 패턴
Python스러운 엣지 케이스 (Edge cases)를 마스터하고 싶다면, 다음 내용들을 반드시 살펴보시기 바랍니다:
-
인스턴스 간에 공유되는 클래스 수준의 가변 속성 (Class-level mutable attributes)
-
함수 내부 리스트에 대한
+=연산자 사용 (제자리 수정 (In-place modification) 대 재바인딩 (Rebinding)) -
루프 변수를 클로저 (Close over)하는 제너레이터 표현식 (Generator expressions)
이 각각의 패턴은 가변 기본 인자와 동일한 근본 원리로 작동합니다. Python의 이름 (Names)이 값을 담는 컨테이너가 아니라 객체에 대한 참조 (References)라는 점을 이해하고 나면, 이 모든 패턴은 완전히 예측 가능해집니다.
기술 면접 준비를 위한 무료 AI 기반 Python 드라이 런 연습 플랫폼인 PyCodeIt의 개발자가 작성하였습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기