여기서는 복사 함수를 외우기보다 지금 새 객체가 필요한지, 아니면 같은 객체를 같이 가리켜도 되는지를 먼저 봅니다.
대입, 얕은 복사, 깊은 복사, 공유는 전부 "참조를 끊어야 하는가"라는 질문으로 갈립니다.
빠른 비교
import copy
original = [[1, 2], [3, 4]]
shallow = original[:] # 또는 copy.copy(original)
deep = copy.deepcopy(original)문법
기본형은 단순 대입, 얕은 복사, 깊은 복사까지 같이 보면 됩니다.
import copy
a = [[1], [2]]
b = a
shallow = a[:]
shallow = copy.copy(a)
deep = copy.deepcopy(a)b = a는 새 객체를 만들지 않고, a[:]나 copy.copy(a)는 바깥 컨테이너만 새로 만들고, copy.deepcopy(...)는 중첩 구조까지 재귀적으로 복제합니다.
바인딩
대입은 복사가 아니라 같은 객체를 가리키는 바인딩이다
Python에서 b = a는 새 리스트를 자동으로 만드는 문법이 아닙니다.
두 이름이 같은 객체를 가리키게 됩니다.
a = [1, 2, 3]
b = a
b.append(4)
print(a) # [1, 2, 3, 4]이 동작을 이해하지 못하면 "왜 다른 변수만 바꿨는데 원본도 바뀌지?"라는 혼란이 자주 생깁니다.
얕은 복사
얕은 복사는 바깥 컨테이너만 새로 만든다
리스트 슬라이싱, list(), copy.copy()는 바깥 리스트만 복사합니다.
안쪽의 중첩 객체는 여전히 공유됩니다.
items = [[1], [2]]
cloned = items[:]
cloned[0].append(99)
print(items) # [[1, 99], [2]]즉 얕은 복사는 "한 단계짜리 구조"에는 충분하지만, 중첩 구조까지 독립시켜 주지는 않습니다.
깊은 복사
깊은 복사는 중첩 구조까지 재귀적으로 복제한다
중첩 리스트나 dict를 통째로 독립시켜야 하면 copy.deepcopy()가 필요할 수 있습니다.
import copy
items = [[1], [2]]
cloned = copy.deepcopy(items)
cloned[0].append(99)
print(items) # [[1], [2]]다만 깊은 복사는 비용이 더 크고, 모든 상황에서 자동 정답은 아닙니다. 정말 독립 복제본이 필요한지 먼저 판단해야 합니다.
공유 판단
복사보다 공유가 더 자연스러운 경우도 많다
모든 변경 가능 객체를 무조건 복사하는 건 과합니다.
읽기 전용으로 같이 쓰거나, 의도적으로 상태를 공유하는 것이 더 자연스러운 경우도 많습니다.
즉 이 카드의 핵심은 "항상 복사해라"가 아니라, 언제 공유를 끊어야 하는가를 구분하는 데 있습니다.
주의할 점
중첩 리스트에서 슬라이싱은 깊은 복사가 아닙니다. 바깥만 새로 생기고 내부 원소는 공유됩니다.
# ❌ 깊은 복사라고 착각하기 쉬움
matrix = [[1, 2], [3, 4]]
cloned = matrix[:]
cloned[0][0] = 99
print(matrix[0][0]) # 99
# ✅ 내부까지 독립시켜야 하면 deepcopy
import copy
matrix = [[1, 2], [3, 4]]
cloned = copy.deepcopy(matrix)
cloned[0][0] = 99
print(matrix[0][0]) # 1참고 링크
2 sources