파이썬은 강력하고 유연한 프로그래밍 언어이지만, 가끔은 예상치 못한 동작으로 개발자를 당황하게 만들기도 합니다. 그중 하나가 함수의 기본 인수로 가변 객체를 사용할 때 발생하는 문제입니다. 이번 글에서는 이러한 문제를 이해하고 해결하는 방법에 대해 알아보겠습니다.
문제 상황
다음과 같은 코드를 살펴보겠습니다.
def surprise(my_list = []):
print(my_list)
my_list.append('x')
surprise()
surprise()
많은 분들은 이 코드의 출력이 다음과 같을 것이라고 예상할 것입니다.
[]
[]
하지만 실제로 코드를 실행해 보면 다음과 같은 결과가 나옵니다.
[]
['x']
왜 이런 일이 발생할까요?
원인 분석
1. 기본 인수는 한 번만 평가됩니다.
파이썬에서 함수의 기본 인수는 함수가 정의될 때 한 번만 평가됩니다. 이는 함수가 호출될 때마다 기본 인수가 새로 생성되는 것이 아니라, 동일한 객체를 참조하게 된다는 의미입니다.
2. 가변 객체의 특성
리스트나 딕셔너리와 같은 가변 객체는 그 내용이 변경될 수 있습니다.
따라서 기본 인수로 가변 객체를 사용하고 이를 수정하면, 해당 객체는 함수 호출 간에 상태를 공유하게 됩니다.
3. 코드의 동작 과정
첫 번째 surprise()
호출:
my_list
는 함수 정의 시 생성된 빈 리스트 []를 참조합니다.print(my_list)
는 []를 출력합니다.my_list.append('x')
를 통해 리스트에 'x'를 추가합니다.
두 번째 `surprise()` 호출:
my_list
는 이미 'x'를 포함하고 있는 동일한 리스트를 참조합니다.print(my_list)
는 ['x']를 출력합니다.my_list.append('x')
를 통해 리스트는 ['x', 'x']가 됩니다.
이처럼 기본 인수로 사용된 리스트는 함수 호출 간에 상태를 유지하며, 이는 예상치 못한 결과를 초래합니다.
해결 방법
이 문제를 해결하려면 기본 인수로 가변 객체를 사용하지 않는 것이 좋습니다. 대신 기본 값을 `None`으로 설정하고, 함수 내부에서 새로운 객체를 생성하는 방법이 일반적입니다.
수정된 코드
def surprise(my_list=None):
if my_list is None:
my_list = []
print(my_list)
my_list.append('x')
surprise()
surprise()
수정된 코드의 출력
[]
[]
이제 각 함수 호출마다 my_list
는 새로운 리스트를 참조하게 되어, 예상한 대로 동작합니다.
핵심 포인트
- 가변 기본 인수 사용은 피해야 합니다. 함수의 기본 인수로 리스트나 딕셔너리와 같은 가변 객체를 사용하면 함수 호출 간에 상태가 공유됩니다.
- 기본 값으로
None
을 사용하세요. 기본 인수를None
으로 설정하고, 함수 내부에서 필요한 경우 새로운 객체를 생성합니다. - 기본 인수는 함수 정의 시점에 평가됩니다. 이는 함수 호출 시점이 아님을 명심해야 합니다.
추가 예시
이 개념을 더욱 이해하기 위해 다른 예시를 살펴보겠습니다.
def add_item(item, item_list=[]):
item_list.append(item)
return item_list
print(add_item(1)) # 출력: [1]
print(add_item(2)) # 출력: [1, 2]
print(add_item(3)) # 출력: [1, 2, 3]
위 코드에서`item_list`는 함수 호출 시마다 동일한 리스트를 참조하므로, 아이템이 계속 누적됩니다.
결론
파이썬에서 함수의 기본 인수로 가변 객체를 사용하는 것은 미묘한 버그를 유발할 수 있습니다. 이를 방지하기 위해서는 기본 인수로 None
을 사용하고, 함수 내부에서 객체를 생성하는 패턴을 따르는 것이 좋습니다. 이러한 작은 습관이 큰 문제를 예방하고, 코드의 안정성과 가독성을 높이는 데 도움이 됩니다.
항상 기본 인수로 가변 객체를 사용할 때는 주의하시기 바랍니다!