파이썬 얕은 복사와 깊은 복사

얕은 복사, 깊은 복사

얕은 복사(shallow copy)

얕은 복사한 변수는 같은 주소를 가리킨다.

a = [1, 2, 3]
b = a # a와 같은 주소를 가리킨다

a의 특정 원소를 변경하면 b도 마찬가지로 변경된 값을 가진다.

a의 특정 원소를 변경하는 것이 a가 가리키는 주소 변경을 일으키지 않기 때문이다.

a[1] = 100
print(b) # b와 a는 여전히 같은 주소를 가리킨다

>>> [1, 100, 3]

깊은 복사(deep copy)

깊은 복사한 변수는 똑같은 값을 가지는 새로운 객체의 주소를 가리킨다.

import copy
a = [1, 2, 3]
b = copy.deepcopy(a) # 새로운 [1, 2, 3] 주소를 가리킨다

(참고) https://thrillfighter.tistory.com/272

정리


b가 a를 어떻게 복사하든, b가 가리키는 주소는 바뀌지 않는다.

복사한 이후부터 a가 가리키는 것(a의 화살표)과 b가 가리키는 것(b의 화살표)은 별개다.

a가 가리키는 주소가 바뀌어도, b는 a를 처음 복사했을 때 주소를 계속 가리키고 있다.

숫자


얕은 복사

하지만 a의 값을 변경시키면 a가 새로운 주소를 가리킨다.

숫자는 불변 객체이기 때문이다.

b가 가리키는 주소와 값은 바뀌지 않는다.

print("*** 숫자 ***")
a = 5
b = a
print(id(5))
print(a, id(a)) # 5 2061182527920
print(b, id(b)) # 5 2061182527920 (a와 같은 주소)
print()

a = 6
print(a, id(a)) # 6 2061182527952 (a의 주소 바뀜)
print(b, id(b)) # 5 2061182527920 (주소 그대로임. 안바뀜)
print()

문자열


얕은 복사

하지만 a의 값을 변경시키면 a가 새로운 주소를 가리킨다.

문자열도 숫자와 마찬가지로 불변 객체이기 때문이다.

b가 가리키는 주소와 값은 바뀌지 않는다.

print("*** 문자열 ***")
a = "hello"
b = a
print(a, id(a)) # hello 2207970515376
print(b, id(b)) # hello 2207970515376 (a와 같은 주소)
print()

a = "hi"
print(a, id(a)) # hi 2061182527952 (a의 주소 바뀜)
print(b, id(b)) # hello 2207970515376 (주소 그대로임. 안바뀜)
print()

리스트


얕은 복사, 특정 원소 변경

a를 얕은 복사한 b는 a와 같은 주소를 가리킨다.

a의 특정 원소를 변경하는 것은 a가 가리키는 주소를 바꾸는 것이 아니다.

주소는 그대로이고 내용물만 덮어씌우기 하는 거다.

따라서 주소가 그대로인 b도 a를 따라 내용물이 변경된다.

print("*** 리스트 ***")
print("얕은 복사한 b는 a의 특정 원소를 변경하는 경우, b도 바뀐다")
a = [1,2,3]
b = a
print(a, id(a)) # [1, 2, 3] 2061184491840
print(b, id(b)) # [1, 2, 3] 2061184491840 (a와 같은 주소)
print()

a[1] = 100
print(a, id(a)) # [1, 100, 3] 2061184491840 (주소 그대로, 값만 바뀜)
print(b, id(b)) # [1, 100, 3] 2061184491840 (주소 그대로, a따라 값 바뀜)
print()

깊은 복사

a와 똑같은 값을 가지는 새로운 객체의 주소를 b는 가지게 된다.

a의 특정 원소를 변경해도 주소가 다르므로 b는 영향을 받지 않는다.

즉, b의 내용물은 그대로이다.

print("깊은 복사한 b는, a와 똑같은 값을 가지는 새로운 리스트의 주소를 가리킨다")
import copy
a = [1,2,3]
b = copy.deepcopy(a)
a[1] = 100
print(a, id(a)) # [1, 100, 3] 1973455816576
print(b, id(b)) # [1, 2, 3] 1973455752128
print()

새로운 리스트 생성

새로운 리스트를 생성하면 새로 생성한 리스트의 주소를 가리키게 된다.

b가 a를 얕은 복사했을 때, a와 같은 주소를 가리키고 있다.

그런데 a가 새로운 리스트의 주소를 가리키는 것으로 바뀌면, b는 a의 영향을 받지 않게 된다.

print("a가 아예 다른 리스트를 가리키는 경우, b는 그대로다")
a = [4, 5, 6]
b = a
print(a, id(a)) # [4, 5, 6] 2412914002624
print(b, id(b)) # [4, 5, 6] 2412914002624 (a와 같은 주소)
print()

a = [7, 8, 9]
print(a, id(a)) # [7, 8, 9] 2412913713472 (a의 주소 바뀜)
print(b, id(b)) # [4, 5, 6] 2412914002624 (주소 그대로임. 안바뀜)
print()

사용자 정의 클래스와 객체


연결리스트 원소 삽입

목표 : 1→2→3 인 연결리스트 head 만들기

사용 변수

  • fixed : 가리키는 주소가 변하지 않는 변수
  • ptr : 계속 새로운 노드의 주소를 가리키는 변수
  • head : 최종 연결리스트
fixed = ptr = ListNode()
for item in [1,2,3]:
	ptr.next = ListNode(item)
	ptr = ptr.next

head = fixed.next
print(head)

새로운 노드 생성

fixed와 ptr은 각각 같은 주소를 가리키고 있다.

for문 : item이 1일 때

ptr.next는 새로운 노드를 가리킨다.

ptr은 방금 생성한 노드를 가리키게 된다.

fixed가 가리키는 주소는 처음 그대로이다. ptr이 가리키는 주소가 변경되는 것과는 별개이다.

for문 : item이 2일 때

for문 : item이 3일 때

print()로 확인하기

fixed = ptr = ListNode()
print(id(fixed), "fixed", fixed)
print(id(ptr), "ptr", ptr)
print()

for item in [1,2,3]:
	ptr.next = ListNode(item)
	print(id(ptr.next), "ptr.next", ptr.next)
	print(id(fixed.next), "fixed.next", fixed.next)
	print(id(fixed), "fixed", fixed)
	
	ptr = ptr.next
	print(id(ptr), "ptr", ptr)
	print(id(fixed), "fixed", fixed)
	
	print()

head = fixed.next
print(head)

잘못된 내용이 있다면 알려주시면 언제든 환영입니다.

좋은 웹페이지 즐겨찾기