[Python] 깊은 복사, 얕은 복사

jake·2022년 10월 7일
0

python

목록 보기
18/20

깊은 복사, 얕은 복사

복사에는 깊은 복사와 얕은 복사라는 개념이 있다.
얕은 복사라는 것은 변수를 복사했지만 실제로는 복사가 아니라 연결돼있는 것을 의미한다.
깊은 복사는 우리가 흔히 알고있는 복사의 개념이다.


얕은 복사

얕은 복사를 하면 복사를 했다고 생각하지만 사실 복사한 것은 참조(메모리 주소)만 복사한 것이지 실제 객체 전체를 복사한것이 아니다.
복사를 해도 같은 메모리 값을 바라보고 있어 객체가 연결되어 있는 것 같다.

a=[1,2,3]
b=a

a.append(4)
print(a) # [1, 2, 3, 4]
print(b) # [1, 2, 3, 4]

b에 a([1,2,3])를 복사하고 그 이후 a에 4를 추가했지만 b에도 자동으로 4가 추가되었다.
a와 b가 같은 메모리 값을 참조하는데 그 메모리 값에 4를 추가하니 자동으로
b에도 값이 추가된 것이다.
이렇게 복사를 했음에도, 값을 변경하면 다른 변수에도 영향을 끼치도록 '참조'만 복사한 것을 얕은 복사라고 한다.

immutable 한 객체들 int, float 등은 얕은 복사를 하던 깊은 복사를 하던 사실 상관이 없다.
왜냐하면 해당 객체들은 값이 변경되면 '무조건' 참조가 변경되기 때문에 얕은 복사를 해서 값을 변경하더라도, 참조하던 다른 객체의 값도 변경되거나 하지 않기 때문이다.

a=1
b=a
a=4

print(id(a)) # 2035693125968
print(id(b)) # 2035693125872

리스트를 예시로 들었던 경우와 달리 a의 값을 변경해도 b의 값이 변경되지 않는다.
리스트(mutable)의 경우 고유한 메모리 값을 할당하여 a와 b가 같은 메모리 값을 참조했지만 int(immutable)의 경우 서로 다른 곳을 바라보기 때문에 값을 변경해도 다른 객체의 값이 변경되지 않는다.


깊은 복사

따라서 얕은 복사와 깊은 복사를 구분해야 하는 객체는 list, set, dictionary와 같은 mutable 객체들이다.

깊은 복사를 하면 원본에 무슨 짓을 하더라도 복사본은 영향을 받지 않는다.

 

방법

얕은 복사 방법

(1) =

a=[1,2,3]
b=a

(2) [:]

a=[1,2,3]
b=a[:]

(3) copy

a=[1,2,3]
b=a.copy()

(4) copy 모듈의 copy

import copy
a=[1,2,3]
b=copy.copy(a)

이러한 방식으로 복사를 하면 a와 b는 서로의 값에 영향을 끼치는 관계가 된다.


깊은 복사 방법

(1) copy 모듈의 deepcopy

import copy
a=[1,2,3]
b=copy.deepcopy(a)

이제 a에 무슨 짓을 해도 b는 영향 받지 않고, 마찬가지로 b에 무슨 짓을 해도 a는 영향 받지 않는다.

 

for

for문에도 깊은 복사와 얕은 복사와 비슷한 개념이 있다.

a=[[] for _ in range(5)]
print(a) # [[], [], [], [], []]

a=[[]]*5
print(a) # [[], [], [], [], []]

두 경우 a는 [[], [], [], [], []]의 형태로 똑같은 값을 가지고 있는 것 같지만 사실 둘은 다른 경우다.

리스트는 그 자체가 하나의 변수가 아니라, 구성 요소들이 저장되어 있는 메모리 상의 위치를 가르키고 있는 형태이다.
[[] for _ in range(5)]의 경우 for문을 따라 새로운 리스트를 5번 만들어 리스트 각각의 메모리 위치를 큰 리스트에 저장한 꼴이다.
반면 [[]]*5의 경우 처음 리스트를 만든 후 그것과 똑같은 리스트를 복사하여 큰 리스트에 저장한 꼴이다.

다시 말해 전자는 큰 리스트 안에 각각의 고유한 리스트가 다섯 개 들어있는 형태이고 후자는 큰 리스트 안에 똑같은 다섯 개의 리스트가 들어있는 형태이다.

a=[[] for _ in range(5)]
print(id(a[0])) # 2674408450816
print(id(a[1])) # 2674408403840


a=[[]]*5
print(id(a[0])) # 2674408451264
print(id(a[1])) # 2674408451264

메모리 값을 출력해보면 확인 된다.
전자는 고유한 리스트이기 때문에 각각의 메모리 값이 다르지만 후자는 똑같은 리스트이기 때문에 메모리 값이 같다.

0개의 댓글