일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 디지털 포렌식 트랙
- 코드엔진
- BoB 12기 최종합격 후기
- Best of the Best
- CodeEngn
- h4ckinggame
- 논문리뷰
- 리버싱
- 코드엔진 베이직
- BoB 12기
- 자살론
- codeengn basic rce 01
- 코드엔진 basic 5
- CodeEngn Basic 01
- 에밀 뒤르켐
- bob
- malware
- 사회분업론
- CodeEngn Basic 5
- 철학
- 사회적 사실
- Today
- Total
SEO
[파이썬] 얕은 복사와 깊은 복사 본문
파이썬에서 데이터를 다룰 때, 객체를 복사하는 방식에는 얕은 복사(Shallow Copy)와 깊은 복사(Deep Copy)라는 두 가지 주요 방법이 있습니다.
1. mutable과 immutable 객체
Python에서는 객체를 크게 mutable(변경 가능) 객체와 immutable(변경 불가능) 객체로 나눌 수 있습니다.
- mutable 객체 : 객체의 값을 변경할 수 있는 객체로, 값을 변경할 수 있기 때문에 복사된 객체와 원본 객체가 같은 메모리 주소를 참조할 수 있습니다.
- EX) list, set, dict
- immutable 객체 : 객체의 값을 변경할 수 없는 객체로, 값을 변경하려고 하면 새로운 객체가 생성됩니다.
- EX) int, str, tuple, frozenset
이렇게 정리하면 무슨 말인지 잘 이해가 안가실 것 같은데요.
a = [1, 2, 3]
b = a
b[0] = 5
print(a, id(a))
print(b, id(b))
위 경우는 mutable 객체 (list)를 사용하였습니다. a 리스트를 b 변수에 할당한 이후에 b 리스트의 원소의 값을 변경하고 비교해보면 데이터와 id 값 모두 같은 것을 확인할 수 있습니다. 즉, 복사된 객체와 원본 객체가 같은 메모리 주소를 참조하고 있는 상태입니다.
a = "abc"
b = a
print(a, id(a))
print(b, id(b))
b = "def"
print(a, id(a))
print(b, id(b))
위 경우에서는 immutable 객체 (str)를 사용하였습니다. 출력값을 보면 값을 변경(def 할당)하기 전까지는 같은 id 값을 공유하는 것을 확인할 수 있습니다. 하지만 값이 변경되면서 기존 변수는 같은 메모리 공간을 차지하고, 변경된 b 변수에 대해 새로운 객체가 생성된 것을 확인할 수 있습니다.
2. 얕은 복사와 깊은 복사
- 얕은 복사 : 객체를 복사할 때, 객체 내부의 참조를 그대로 복사합니다. 즉, 외부 객체만 복사하고, 내부에 포함된 객체들은 복사하지 않고 참조만 합니다.
=> 아래 코드를 바탕으로 이해하시면 더 쉬울 거에요!
2-1. 얕은 복사 예시
a = [1, 2, 3]
b = a[:]
print(id(a))
print(id(b))
print(a == b)
print(a is b)
위처럼 리스트 슬라이싱을 사용하여 얕은 복사를 수행할 수 있습니다. 위의 경우, 외부 객체를 복사하여 새로운 객체가 생성되었습니다. 그렇기 때문에 둘의 데이터는 같지만(True) 같은 객체는 아닙니다(False).
2-2. 내부 객체의 참조
a = [[1, 2], [3, 4]]
b = a[:]
print(id(a))
print(id(b))
print(id(a[0]))
print(id(b[0]))
print(a == b)
print(a is b)
print(a[0] is b[0])
이번에는 2차원 배열을 예시로 설명하겠습니다. 리스트 슬라이싱을 통해 얕은 복사를 수행하였는데, 각 변수에 할당된 id 값은 다르고 b는 a의 복사본이지만 내부 리스트([1,2]와 [3,4])는 같은 참조를 공유하고 있습니다.
보통 데이터 복사는 원본은 그대로 두고, 값을 수정하고 싶을 때 많이 사용하는데 저는 이 부분에 대한 지식이 부족했어서 아래 문제 푸는데 좀 힘들었습니다 하하 (계속 엥 왜 오류가 나지를 반복한...)
https://scorchingnraining.tistory.com/181
- 깊은 복사 : 객체와 그 내부에 포함된 모든 객체들을 완전히 새롭게 복제합니다. 즉, 내부 객체들도 새로운 메모리 공간에 할당되어 원본 객체에 영향을 주지 않습니다.
2-3. 깊은 복사 예시
import copy
a = [[1, 2], [3, 4]]
b = copy.deepcopy(a)
b[1].append(5)
print(a)
print(b)
파이썬의 copy.deepcopy() 메소드를 사용하면 깊은 복사를 수행할 수 있습니다. a와 내부 리스트까지 모두 새롭게 복사하기 때문에 b의 값을 바꿀 경우, a의 내용은 변하지 않고 b의 값만 변경됩니다. 즉, 완적히 독립적인 상태를 유지할 수 있습니다.
얕은 복사와 비교해보면:
a = [[1, 2], [3, 4]]
b = a
b[1].append(5)
print(a)
print(b)
a 값까지 변경된 것을 확인할 수 있습니다.
메소드 호출이 어려울 때 다음과 같은 방법으로도 깊은 복사를 할 수 있습니다.
a = [[1, 2], [3, 4]]
b = [row[:] for row in a] #깊은 복사
b[1].append(5)
print(a, id(a[0]))
print(b, id(b[0]))
위 코드가 깊은 복사가 가능한 이유는 a의 각 내부 리스트 [1,2], [3,4]를 새로운 리스트 객체로 복사하기 때문입니다. a[0]은 [1, 2]라는 원본 객체이고, b[0]은 그 복사본인 새로운 리스트 [1, 2]가 되게됩니다. 즉, 모든 중첩된 객체들이 새로 복사되었기 때문에 깊은 복사로 동작합니다.
3. 얕은 복사와 깊은 복사의 차이점
- 얕은 복사: 객체의 첫 번째 수준만 복사하고, 그 내부 객체들은 참조만 복사합니다. 내부 객체가 mutable이라면, 변경 시 원본 객체에 영향을 미칠 수 있습니다.
- 깊은 복사: 객체와 그 내부 객체들까지 모두 새롭게 복사합니다. 변경 시 원본 객체에 영향을 미치지 않으며, 독립적인 객체로 취급됩니다.
참고자료