리스트를 복사했는데 원본도 바뀔 때: 얕은 복사와 deepcopy

이 카테고리 글 목록

전체글 보기

list.copy()는 바깥 리스트만 새로 만들고 안의 객체는 공유하는 얕은 복사다. 복사한 리스트 안의 중첩 값을 바꾸면 원본에서도 보일 수 있다. other = original은 바깥 리스트조차 복사하지 않는 할당이고, 중첩 객체도 복사해야 할 때는 copy.deepcopy의 계약을 확인한다.

복사했는데 원본도 바뀌는 최소 예제

original = [["초안"]]
copied = original.copy()
copied[0].append("수정")
print(original)
print(copied is original)
print(copied[0] is original[0])
[['초안', '수정']]
False
True

두 바깥 리스트는 다르지만, 0번 칸은 같은 안쪽 리스트를 가리킨다. copied[0].append는 그 공유 객체를 바꿨다. 이 예제에서 확인할 것은 출력 값뿐 아니라 is로 확인한 객체의 동일성이다.

각 Python 코드 블록은 import와 입력을 포함한 독립 예제다. CPython 3.14.3·Windows 11에서 실행했고 Python 3.14 공식 문서와 대조했다. 문서 사이트의 패치 판본은 실행한 인터프리터와 다를 수 있다.

할당·얕은 복사·깊은 복사를 나란히 확인하기

from copy import deepcopy

original = [{"labels": ["초안"]}]
assigned = original
shallow = original.copy()
deep = deepcopy(original)
print("assigned:", assigned is original)
print("shallow:", shallow is original, shallow[0] is original[0])
print("deep:", deep is original, deep[0] is original[0])

shallow.append({"labels": ["별도"]})
shallow[0]["labels"].append("수정")
print("lengths:", len(original), len(shallow))
print("original:", original)
print("deep:", deep)
assigned: True
shallow: False True
deep: False False
lengths: 1 2
original: [{'labels': ['초안', '수정']}]
deep: [{'labels': ['초안']}]

shallow에 항목을 추가한 일은 새 바깥 리스트만 바꿔 길이가 2가 됐다. 반면 공유한 사전 안의 labels 리스트를 수정한 일은 original에도 보인다. 이 예제의 deep은 그 사전과 안쪽 리스트도 복사해 원본 수정과 분리됐다.

하려는 일 선택 확인할 점
같은 리스트를 다른 이름으로 사용 할당 추가·삭제도 같은 객체에 반영
바깥 리스트의 순서·항목만 따로 변경 얕은 복사 안쪽 객체는 공유
중첩된 변경 가능한 객체도 복사 deepcopy 검토 내부 공유 관계와 객체별 지원 확인

copy 공식 문서는 할당이 객체를 복사하지 않는다는 점과 두 복사의 차이를 설명한다. list의 슬라이스 original[:]나 list(original)로 바깥 리스트를 새로 만들어도 중첩 객체까지 자동 복사하는 것은 아니다.

깊은 복사도 내부의 공유 관계를 없애지는 않는다

from copy import deepcopy

shared = ["원본"]
original = [shared, shared]
cloned = deepcopy(original)
print(cloned[0] is original[0])
print(cloned[0] is cloned[1])
cloned[0].append("복사본 수정")
print(original)
print(cloned)
False
True
[['원본'], ['원본']]
[['원본', '복사본 수정'], ['원본', '복사본 수정']]

원본에서 두 칸이 하나의 리스트를 공유했다. 깊은 복사에서는 새 안쪽 리스트가 생겼지만 복사본의 두 칸도 그 새 리스트를 함께 참조한다. deepcopy는 memo를 사용해 이미 복사한 객체를 기억한다. 모든 등장 위치에 서로 독립적인 객체를 만드는 연산이라고 해석하면 안 된다.

파일·소켓 같은 자원을 일반 리스트처럼 복제할 수 있다고 가정하지 않는다. 함수·클래스처럼 그대로 반환되는 객체도 있고, 사용자 정의 클래스는 복사 동작을 정할 수 있다. 필요한 필드만 분리하면 되는지, 공유가 의도된 것인지부터 판단한다. 이 글의 리스트·사전·문자열 예제가 임의의 외부 자원이나 사용자 정의 객체의 복사를 검증한 것은 아니다.

이 카테고리의 다른 글

전체글 보기

“리스트를 복사했는데 원본도 바뀔 때: 얕은 복사와 deepcopy”에 대한 2개의 생각

댓글 남기기