파이썬에서 데이터를 복사할 때 얕은 복사(Shallow Copy)와 깊은 복사(Deep Copy)의 차이를 명확히 이해하는 것은 중요한 개념입니다. 두 방식은 특히 리스트나 딕셔너리 같은 가변 객체(Mutable Object)를 다룰 때 데이터 처리의 동작 방식을 크게 좌우합니다. 이 글에서는 이 두 복사 방식의 동작 원리, 차이점, 그리고 올바르게 사용하는 방법에 대해 알아보겠습니다.
1. 객체의 복사란 무엇인가?
객체를 복사한다는 것은 특정 데이터의 새로운 사본을 생성하는 것을 의미합니다. 복사의 목적은 원본 데이터를 변경하지 않고 독립적으로 사용하거나, 특정 작업을 수행한 뒤 원본과 복사본을 비교하려는 경우가 많습니다.
2. 얕은 복사(Shallow Copy)
얕은 복사는 가장 바깥쪽 객체만 복사하고, 내부의 중첩된 객체들은 원본과 동일한 참조(reference)를 공유하는 복사 방식입니다.
[동작 방식]
- 새 객체를 생성합니다.
- 복사 대상의 최상위 데이터 구조(리스트, 딕셔너리 등)만 복사하고, 내부 요소는 기존 객체의 참조를 그대로 사용합니다.
예제 코드
import copy
# 원본 리스트
original = [[1, 2, 3], [4, 5, 6]]
# 얕은 복사
shallow_copied = copy.copy(original)
# 원본과 복사본의 내용 확인
print("원본:", original) # [[1, 2, 3], [4, 5, 6]]
print("얕은 복사:", shallow_copied) # [[1, 2, 3], [4, 5, 6]]
# 내부 요소 수정
shallow_copied[0][0] = 99
# 원본과 복사본 모두 영향받음
print("수정 후 원본:", original) # [[99, 2, 3], [4, 5, 6]]
print("수정 후 얕은 복사:", shallow_copied) # [[99, 2, 3], [4, 5, 6]]
핵심 포인트
- 리스트 original과 shallow_copied는 서로 다른 객체이지만, 내부 리스트 original[0]과 shallow_copied[0]는 같은 객체를 참조합니다.
- 따라서 shallow_copied[0][0]을 수정하면 원본도 영향을 받습니다.
3. 깊은 복사(Deep Copy)
깊은 복사는 모든 계층의 데이터를 재귀적으로 복사하여 원본과 완전히 독립적인 객체를 생성하는 방식입니다.
[동작 방식]
- 새 객체를 생성합니다.
- 복사 대상의 최상위 데이터 구조와 내부에 중첩된 모든 객체를 새로운 객체로 재귀적으로 복사합니다.
예제 코드
import copy
# 원본 리스트
original = [[1, 2, 3], [4, 5, 6]]
# 깊은 복사
deep_copied = copy.deepcopy(original)
# 내부 요소 수정
deep_copied[0][0] = 99
# 원본은 영향을 받지 않음
print("수정 후 원본:", original) # [[1, 2, 3], [4, 5, 6]]
print("수정 후 깊은 복사:", deep_copied) # [[99, 2, 3], [4, 5, 6]]
핵심 포인트
- 리스트 original과 deep_copied는 완전히 독립적인 객체입니다.
- deep_copied[0][0]을 수정해도 원본은 영향을 받지 않습니다.
4. 얕은 복사 vs 깊은 복사의 차이
구분얕은 복사깊은 복사
얕은 복사 | 깊은 복사 | |
복사 대상 | 최상위 객체만 복사 | 최상위 객체와 모든 중첩 객체를 재귀적으로 복사 |
참조 공유 | 내부 객체는 원본과 참조를 공유 | 내부 객체도 복사하여 참조를 공유하지 않음 |
수정 영향 | 내부 객체 수정 시 원본도 영향을 받음 | 내부 객체 수정 시 원본에 영향을 주지 않음 |
속도 | 상대적으로 빠름 | 상대적으로 느림 |
5. 언제 어떤 방식을 사용할까?
- 얕은 복사:
- 데이터 구조가 단순하거나, 내부 객체를 수정하지 않는 경우 적합합니다.
- 성능이 중요할 때 사용됩니다.
- 깊은 복사:
- 데이터 구조가 중첩되어 있고, 내부 객체의 독립성을 유지해야 할 때 필요합니다.
- 원본 데이터를 절대 변경해서는 안 되는 경우 사용됩니다.
6. 자주 하는 실수와 주의점
- 가변 객체의 참조 문제
- 가변 객체의 얕은 복사는 원본 데이터를 의도치 않게 변경할 수 있습니다.
- 예: 리스트, 딕셔너리, NumPy 배열 등이 해당됩니다.
- copy.copy()와 copy.deepcopy()의 혼용
- copy.copy()는 얕은 복사를 수행합니다.
- copy.deepcopy()를 사용할 때는 성능에 주의하세요. 중첩된 데이터 구조가 클수록 복사 속도가 느려질 수 있습니다.
7. 실전 응용: 얕은 복사와 깊은 복사의 올바른 활용
(1) 딕셔너리 데이터 복사
import copy
original = {"a": [1, 2, 3], "b": [4, 5, 6]}
shallow_copied = copy.copy(original)
deep_copied = copy.deepcopy(original)
shallow_copied["a"][0] = 99
print("원본:", original) # {'a': [99, 2, 3], 'b': [4, 5, 6]}
print("얕은 복사:", shallow_copied) # {'a': [99, 2, 3], 'b': [4, 5, 6]}
deep_copied["a"][0] = 42
print("원본:", original) # {'a': [99, 2, 3], 'b': [4, 5, 6]}
print("깊은 복사:", deep_copied) # {'a': [42, 2, 3], 'b': [4, 5, 6]}
(2) NumPy 배열 복사
import numpy as np
original = np.array([[1, 2, 3], [4, 5, 6]])
shallow_copied = original.copy() # NumPy의 기본 복사는 얕은 복사
deep_copied = np.array(original, copy=True) # NumPy에서 명시적 깊은 복사
shallow_copied[0, 0] = 99
print("원본:", original) # [[99 2 3]
# [ 4 5 6]]
print("얕은 복사:", shallow_copied) # [[99 2 3]
# [ 4 5 6]]
8. 결론
파이썬의 얕은 복사와 깊은 복사는 데이터 처리에 있어 매우 중요한 개념입니다. 특히 중첩된 데이터 구조를 다룰 때는 올바른 복사 방식을 선택하지 않으면 예상치 못한 버그가 발생할 수 있습니다. 이번 글에서 다룬 내용과 예제를 바탕으로 상황에 맞는 복사 방식을 적절히 활용하시기 바랍니다.
'파이썬' 카테고리의 다른 글
Streamlit/GPT API 이용해보기 (0) | 2023.11.22 |
---|---|
파이썬 예외처리 (0) | 2023.09.17 |
파이썬 스페셜 메소드 (0) | 2023.09.17 |
파일 입출력 라이브러리2 (0) | 2023.09.14 |
파일 입출력 라이브러리 (0) | 2023.09.14 |