[Python] 할당과 복사 / 얕은 복사, 깊은 복사 (shallow copy, deep copy)

kkamyang·2021년 12월 31일
3
post-thumbnail

📍할당과 복사

할당과 복사는 비슷해보이지만 차이가 있다. 비교를 위해 먼저 리스트를 다른 변수에 할당해보자!

리스트 a를 b에 할당

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

리스트를 다른 변수에 할당하였으므로 리스트가 두개가 될 것 같지만 확인해보면 다음과 같이 리스트는 하나이다.

두 리스트를 연산자로 확인해보면 True가 나온다.

이름은 다르지만 사실은 같은 리스트

a is b 
-> True

따라서 b의 요소를 변경하면 a도 변경이 된다.

b의 1번 인덱스를 5로 변경

b[1] = 5
print(a)
-> [1, 5, 3]
print(b)
-> [1, 5, 3]

이해하기 쉽게 그림으로 확인해보면 다음과 같다.

다음은 복사를 해보자!
이번에도 똑같은 리스트를 할당이 아닌 복사를 해보면 다음과 같다.

a를 b로 복사

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

이번엔 어떻게 되었을까..?
오.. 뭔가 다르다
앞에서 나온 할당 그림과 비교했을 때 리스트가 각각 생긴 걸 확인할 수 있다.
정말 서로 다른 녀석인지 확인해봐야겠다.

a와 b를 연산자로 비교

a is b
-> False
a == b
-> True

a 와 b 는 엄연히 다른 객체이지만 a를 그대로 복사했기 때문에 리스트의 요소는 모두 같으므로 ==로 비교해보면 같다고 나온다.

그럼 리스트의 요소를 변경해주면 a == b 도 False 가 나오겠군

b의 요소 하나를 바꿔보기

b[1] = 5
print(a)
-> [1, 2, 3]
print(b)
-> [1, 5, 3]

리스트 b는 변경이 되었지만 a는 그대로이다.

할당을 하면 b의 요소를 변경하면 a도 똑같이 변경이 되었다. 하지만 복사를 하면 a에는 아무런 영향을 미치지 않았다. 그림을 보면 쉽게 이해된다.

그러면 튜플을 이용해서 할당과 복사를 하면 어떻게 될까?
궁금하면 해보기

튜플 할당하기

a = (1, 2, 3)
b = a

튜플 복사하기

a = (1, 2, 3)
b = a.copy()
-> AttributeError: 'tuple' object has no attribute 'copy'

할당은 문제없지만 복사를 하면 에러가 발생한다!
이유는 리스트는 생성된 후 변경이 가능한 객체이지만 튜플은 변경이 불가능한 객체이기 때문이다. 이를 파이썬에서는 가변 객체(mutable), 불변 객체(immutable)라고 부른다.

📍mutable, immutable 객체

객체가 생성된 후 변경이 가능하면 mutable객체, 불가능하면 immutable객체라고 한다. 다음의 객체 구분 표를 보면 더 쉽게 구분할 수 있다.

위에서 실행한 튜플 할당을 다시 한 번 보자.

튜플 할당하기

a = (1, 2, 3)
b = a

튜플은 변경이 불가능한 객체이기 때문에 b의 요소를 바꿨을 때 a도 바뀌는지 확인할 필요가 없다. 애초에 불가능하니까! 하지만 재할당을 하는 경우에는 어떻게 될까?

튜플 재할당하기

a = (1, 2, 3)
b = a
b = (4, 5)

이렇게 변수에 다시 할당하는 것을 재할당이라고 한다. 재할당 한 결과를 보니 a에는 영향을 주지 않는 것을 확인할 수 있다.

immutable 객체를 복사하거나 변경한다면 에러 코드가 떠서 알아차릴 수 있지만 mutable 객체는 그렇지 않다. 따라서 나도 모르게 변경되어 버리는 경우가 생길 수도 있기 때문에 얕은 복사, 깊은 복사에 대한 개념을 잘 이해해야 한다.

📍얕은 복사(shallow copy)

먼저 예시부터 들어본다. copy라는 모듈에 있는 얕은 복사를 사용해보자!

import copy

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

print(b)
-> [1, 2, [3, 4]]

출력 결과 제대로 복사가 되었다. 그러면 이번에는 b의 요소를 변경해보자.

b[0] = 5

print(b)
-> [5, 2, [3, 4]]
print(a)
-> [1, 2, [3, 4]]

확인해보면 b의 요소만 바뀌었고 a는 그대로이다.

이번에는 리스트 안에 있는 리스트의 요소를 변경해보자.

import copy

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

b[2].append(5)

print(b)
-> [1, 2, [3, 4, 5]]
print(a)
-> [1, 2, [3, 4, 5]]

엥..? 이번엔 a도 변경되었다. 이유는 무엇일까??

그림을 보면 이해하기가 편하다.
먼저 처음에 복사해주었을 때의 그림이다.

0,1번 인덱스인 1과 2는 각각 리스트가 따로 생성되어 있다.
하지만 리스트안의 리스트인 [3, 4]는 할당했을 때처럼 같은 리스트를 갖게 된다.

따라서 첫 번째처럼 바깥 리스트의 요소를 변경하면 다음과 같이 되고,

두 번째처럼 리스트 안의 리스트의 요소를 바꿔주면 다음과 같이 되는 것이다.

결국엔 얕은 복사는 가장 바깥에 있는 껍질만 복사를 하는 것이다.
따라서 안쪽 요소들까지 복사를 하고 싶다면 깊은 복사를 해주어야 한다.

📍깊은 복사(deep copy)

깊은 복사 역시 copy 모듈에 있는 기능을 이용한다.

import copy

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

b[2].append(5)

print(b)
-> [1, 2, [3, 4, 5]]
print(a)
-> [1, 2, [3, 4]]

얕은 복사와는 다르게 깊은 복사에서는 a가 변경되지 않는 것을 확인할 수 있다.

그림으로 확인해보면 다음과 같다.

출처:
https://dojang.io/mod/page/view.php?id=2282
https://wikidocs.net/16038

profile
공부 정리정돈 📚 💻

0개의 댓글