가변 객체의 참조 전파 — 복사의 함정
이 토픽을 마치면
Python에서 리스트를 =로 복사하면 왜 원본이 바뀌는지 이해하고, 얕은 복사와 깊은 복사를 구분해서 사용할 수 있습니다.
복사했는데 원본이 바뀐다
original = [1, 2, 3]copy = original
copy.append(4)print(original) # [1, 2, 3, 4] — 원본도 바뀜!copy = original은 복사가 아닙니다. 같은 리스트에 이름표를 하나 더 붙인 것입니다. original과 copy는 같은 리스트를 가리키는 두 개의 이름입니다.
이전 토픽(변수와 메모리)에서 변수는 "값을 담는 상자"가 아니라 "객체를 가리키는 화살표"라고 배웠습니다. 이것이 실전에서 문제를 일으키는 순간이 바로 여기입니다.
print(id(original)) # 4392957504print(id(copy)) # 4392957504 — 같은 주소!진짜 복사 — 슬라이싱
original = [1, 2, 3]copy = original[:] # 전체 슬라이싱 = 새 리스트
copy.append(4)print(original) # [1, 2, 3] — 원본 안 바뀜print(copy) # [1, 2, 3, 4][:]는 리스트의 처음부터 끝까지 잘라서 새 리스트를 만듭니다. list(original)이나 original.copy()도 같은 효과입니다.
얕은 복사의 한계
matrix = [[1, 2], [3, 4]]shallow = matrix[:]
shallow[0][0] = 99print(matrix) # [[99, 2], [3, 4]] — 원본도 바뀜!슬라이싱은 1단계만 복사합니다. matrix[:]는 바깥 리스트는 새로 만들지만, 안의 [1, 2]와 [3, 4]는 같은 객체를 공유합니다. 안쪽을 수정하면 원본에도 반영됩니다.
이것이 **얕은 복사(shallow copy)**입니다.
깊은 복사 — copy.deepcopy
import copy
matrix = [[1, 2], [3, 4]]deep = copy.deepcopy(matrix)
deep[0][0] = 99print(matrix) # [[1, 2], [3, 4]] — 원본 안 바뀜deepcopy는 내부 객체까지 전부 재귀적으로 복사합니다. 완전히 독립된 새 객체입니다.
언제 뭘 쓸까
| 상황 | 방법 | 이유 |
|---|---|---|
| 1차원 리스트 | lst[:] 또는 lst.copy() | 빠르고 간단 |
| 2차원 이상 중첩 리스트 | copy.deepcopy(lst) | 내부 리스트까지 독립 필요 |
| 딕셔너리 | dict.copy() 또는 {**d} | 1단계 얕은 복사 |
| 중첩 딕셔너리 | copy.deepcopy(d) | 내부 dict/list까지 독립 |
| 정말 복사 안 해도 되는 경우 | = 그대로 | 의도적으로 같은 객체를 공유할 때 |
함수 인자에서도 똑같이
def add_item(lst, item): lst.append(item) return lst
my_list = [1, 2, 3]result = add_item(my_list, 4)
print(my_list) # [1, 2, 3, 4] — 원본이 바뀜함수에 리스트를 전달하면 참조가 전달됩니다. 함수 안에서 .append()하면 원본도 바뀝니다. 원본을 보존하려면 함수 안에서 복사를 먼저 하세요:
def add_item_safe(lst, item): new_lst = lst[:] new_lst.append(item) return new_lst가변 vs 불변 정리
| 타입 | 가변/불변 | = 복사 후 수정 시 원본 영향 |
|---|---|---|
int, float, str, tuple | 불변 | 영향 없음 (어차피 새 객체 생성) |
list, dict, set | 가변 | 원본도 바뀜 |
불변 객체는 이 문제가 발생하지 않습니다. 문자열을 "수정"하면 항상 새 문자열이 만들어지니까요. 가변 객체에서만 주의가 필요합니다.
핵심
=는 복사가 아니라 같은 객체에 이름을 하나 더 붙이는 것입니다. 1차원은[:]나.copy(), 중첩 구조는copy.deepcopy(). 함수에 가변 객체를 넘기면 원본이 바뀔 수 있습니다 — 의도하지 않았다면 먼저 복사하세요.