1. 값에 의한 전달과 참조에 의한 전달의 차이
Python에서는 함수에 인수를 전달하는 방법이 두 가지 있습니다: 값에 의한 전달(pass‑by‑value)과 참조에 의한 전달(pass‑by‑reference).
- 값에 의한 전달 : 값의 복사본이 함수에 인수로 전달되는 방식; 함수 내부에서 매개변수를 수정해도 원본 변수에는 영향을 주지 않습니다.
- 참조에 의한 전달 : 변수에 대한 참조(주소)가 함수에 전달되는 방식; 함수 내부에서 이루어진 변경이 원본 변수에 반영됩니다.
Python에서는 객체의 특성에 따라 이 동작이 달라집니다. 특히 가변(mutable) 데이터 타입에 대해서는 “참조에 의한 전달” 동작이 적용되어 코드 동작에 큰 영향을 미치므로 정확히 이해하는 것이 중요합니다.
2. Python 객체의 특성
Python에서는 모든 데이터가 객체로 취급되며, 객체의 특성에 따라 불변(immutable, 변경 불가) 객체와 가변(mutable, 변경 가능) 객체로 구분됩니다. 이 구분은 함수에 인수를 전달할 때 객체가 어떻게 동작하는지에 영향을 줍니다.
- 불변 객체 : 생성 후에 수정할 수 없는 객체. 예시로 정수(
int), 부동소수점(float), 문자열(str), 튜플(tuple) 등이 있습니다. 이러한 데이터 타입은 한 번 생성되면 변경할 수 없으므로, 함수 내부에서 해당 객체를 참조하더라도 원본에는 영향을 주지 않습니다. - 가변 객체 : 생성 후에 수정할 수 있는 객체. 예시로 리스트(
list), 사전(dict), 집합(set) 등이 있습니다. 함수 내부에서 이러한 객체를 수정하면 그 변경이 호출자에게도 반영되므로, Python의 참조 전달 동작에 크게 영향을 받습니다.

3. Python에서 인수 전달 방식
Python에서는 함수를 호출할 때 객체에 대한 참조가 전달됩니다. 이 동작은 종종 “객체 참조에 의한 전달(pass‑by‑object‑reference)”이라고 불립니다. 아래에서는 객체가 불변인지 가변인지에 따라 Python이 어떻게 동작하는지 구체적인 예시를 통해 살펴보겠습니다.
3.1 불변 객체
불변 객체를 함수에 전달하면, 함수 내부에서 새로운 값을 할당할 경우 새로운 객체가 생성되고 원본 객체에는 영향을 주지 않습니다.
예시: 불변 정수 전달
def modify_number(num):
num = num * 2
print("Number inside the function:", num)
original_num = 5
modify_number(original_num)
print("Number outside the function:", original_num)
Output:
Number inside the function: 10
Number outside the function: 5
Explanation
modify_number 함수 내부에서 변수 num을 두 배로 만들지만, 이는 함수 밖의 original_num에 영향을 주지 않습니다. 정수는 불변이므로 새로운 값이 할당될 때 새로운 객체가 생성되고, original_num은 그대로 유지됩니다.
3.2 가변 객체
가변 객체를 함수에 전달하면, 함수 내부에서도 동일한 참조가 사용됩니다. 따라서 함수 내부에서 이루어진 변경이 원본 객체에도 반영됩니다.
예시: 가변 리스트 전달
def add_item(my_list):
my_list.append("Added element")
print("List inside the function:", my_list)
original_list = ["Element 1", "Element 2"]
add_item(original_list)
print("List outside the function:", original_list)
Output:
List inside the function: ['Element 1', 'Element 2', 'Added element']
List outside the function: ['Element 1', 'Element 2', 'Added element']
Explanation
리스트는 가변이므로 add_item 함수 내부에서 이루어진 변경이 함수 밖의 original_list에도 바로 반영됩니다. 이렇게 가변 객체는 함수 내부와 외부가 동일한 객체를 공유하므로, 함수 내부에서의 수정이 호출자에게도 영향을 미칩니다.
4. 참조에 의한 전달 시 주의사항 및 대처법
Python에서 가변 객체를 함수에 전달하면 예상치 못한 동작이 발생할 수 있습니다. 이러한 문제를 방지하기 위한 몇 가지 대책을 소개합니다.
4.1 객체 복사 사용
함수 내부에서 객체를 수정하고 싶다면, 변경하기 전에 객체를 복사하여 원본에 영향을 주지 않도록 할 수 있습니다. Python에서는 copy 모듈을 사용해 얕은 복사(copy.copy) 또는 깊은 복사(copy.deepcopy)를 만들 수 있습니다.
예시: 깊은 복사를 사용해 가변 객체 문제 방지
import copy
def add_item(my_list):
my_list_copy = copy.deepcopy(my_list)
my_list_copy.append("Added element")
print("List inside the function (copy):", my_list_copy)
original_list = ["Element 1", "Element 2"]
add_item(original_list)
print("List outside the function:", original_list)
Output:
List inside the function (copy): ['Element 1', 'Element 2', 'Added element']
List outside the function: ['Element 1', 'Element 2']
Explanation
deepcopy을 사용하면 함수 내부에서 새로운 객체가 생성되어 원본 리스트가 영향을 받지 않습니다. 이렇게 하면 함수 안과 밖에서 독립적인 변경이 가능합니다.
4.2 기본 인수에 None 사용
가변 객체를 함수의 기본 인수로 설정하는 것을 피하고, 기본값으로 None을 사용한 뒤 함수 내부에서 새로운 객체를 생성하는 것이 권장됩니다.
예시: 기본 인수를 None으로 안전하게 설정하는 방법
def add_item(my_list=None):
if my_list is None:
my_list = []
my_list.append("New element")
print("List inside the function:", my_list)
return my_list
# When no list is passed
result = add_item()
print("Returned list:", result)
# When a list is passed
existing_list = ["Element 1", "Element 2"]
result = add_item(existing_list)
print("Returned list:", result)
print("Original list:", existing_list)
Output:
List inside the function: ['New element']
Returned list: ['New element']
List inside the function: ['Element 1', 'Element 2', 'New element']
Returned list: ['Element 1', 'Element 2', 'New element']
Original list: ['Element 1', 'Element 2', 'New element']
Explanation
기본 인수로 None을 사용하고 함수 내부에서 새로운 리스트를 생성하면 외부 리스트가 예기치 않게 수정되는 것을 방지할 수 있습니다. None을 기본값으로 지정하면 새 리스트를 만들지 기존 리스트를 사용할지 명확히 구분할 수 있습니다.

5. 실전 예제: Pass-by-Reference 이해를 깊게 하기 위한 코드
지금까지 배운 내용을 실전 예제로 정리해 보겠습니다.
예시: 딕셔너리의 키와 값 수정하기
딕셔너리는 가변 객체이므로 함수 내부에서 수행된 작업이 함수 외부의 딕셔너리에도 영향을 미칩니다. 아래 예제는 딕셔너리의 키와 값을 변경할 때의 동작을 보여줍니다.
def modify_dict(my_dict):
my_dict["new key"] = "new value"
print("Dictionary inside the function:", my_dict)
original_dict = {"key1": "value1", "key2": "value2"}
modify_dict(original_dict)
print("Dictionary outside the function:", original_dict)
Output:
Dictionary inside the function: {'key1': 'value1', 'key2': 'value2', 'new key': 'new value'}
Dictionary outside the function: {'key1': 'value1', 'key2': 'value2', 'new key': 'new value'}
Explanation
딕셔너리는 가변이기 때문에 함수 내부에서 추가된 내용이 함수 외부의 original_dict에도 반영됩니다. 리스트와 마찬가지로 딕셔너리도 참조에 의해 전달되므로 호출자에게 영향을 줄 수 있습니다.
6. 요약
Python의 “pass-by-reference” 동작은 객체의 특성에 따라 다르게 나타나므로, 이를 이해하는 것이 코드 신뢰성을 높이는 데 필수적입니다. 특히 가변 객체에 대한 참조 전달로 인한 예상치 못한 동작을 방지하려면 복사와 기본 인수 None 사용을 적극 활용하는 것이 좋습니다.
핵심 요점
- 값에 의한 전달과 참조에 의한 전달의 차이 : 파이썬에서는 함수 내부에서의 수정이 원래 변수에 영향을 미치는지는 객체의 특성에 따라 달라집니다.
- 불변 vs. 가변 : 파이썬의 불변 객체는 함수 내부에서 이루어진 변경이 반영되지 않으며, 가변 객체는 참조 전달을 통해 함수 내부와 외부에서 공유됩니다.
- 완화 전략 : 가변 객체의 경우
copy모듈의deepcopy를 사용하거나 기본 인자를None으로 설정하는 등의 방법이 예기치 않은 동작을 방지하는 데 효과적입니다.
이 글을 통해 파이썬에서의 참조 전달에 대한 이해가 깊어졌기를 바랍니다. 이 지식을 활용해 효율적이고 오류가 적은 코드를 작성하세요.




