파이썬의 중요한 특징 중 하나는 모든 것이 객체라는 점이다. 심지어 문자, 숫자까지도 모두 객체다. 그러다 보니 별도로 값을 복사하지 않는 이상 번수에 값을 할당하는 모든 행위는 값 객체에 대한 참조가 된다.
이말은 참조가 가리키는 원래의 값을 변경하면 모든 참조, 즉 모든 변수의 값 또한 함께 변경된다는 말이다.
list의 슬라이싱을 통한 새로운 값을 할당해보면 아래의 결과와 같이 슬라이싱을 통해서 값을 할당하면 새로운 id가 부여되며, 서로 영향을 받지 않는다.
a = [1,2,3]
b = a[:]
>>> id(a)
4396179528
>>> id(b)
4393788808
>>> a == b
True
>>> a is b
False
>>> b[0] = 5
>>> a
[1, 2, 3]
>>> b
[5, 2, 3]
이러한 슬라이싱 또한 얕은 복사에 해당한다.
copy 모듈의 copy 메소드 또한 얕은 복사이다. a를 중첩 리스트(nested list)로 만들고 얕은 복사로 b에 변수간 대입을 한다. 이후 a[1]에 값을 추가하여 a 내부 첫 번째 리스트 [1,2]에서 세번째 원소인 5를 추가하였다.
a의 값만 변경했는데, b도 같은 값으로 변경되었다. 변수 a와 변수 b가 가리키는 메모리 주소도 서로 다르지만 이는 a[0]과 b[0]이 가리키는 주소는 똑같다기 때문이다. 즉, python에서 mutable 변수 내부에 또 mutable이 있는 경우, 얕은 복사로는 mutable 내부의 mutable의 메모리 주소는 달라지지 않는다는 것이다.
결국엔 얕은 복사는 가장 바깥에 있는 껍질만 복사를 하는 것이다. 따라서 안쪽 요소들까지 복사를 하고 싶다면 깊은 복사를 해주어야 한다.
import copy
a = [[1,2],[3,4]]
b = copy.copy(a)
a[1].append(5)
>>> a
[[1, 2], [3, 4, 5]]
>>> b
[[1, 2], [3, 4, 5]]
깊은 복사는 내부에 객체들까지 모두 새롭게 copy 되는 것이다. 즉, 중첩된 mutable 변수에 대해서도 완전히 독립성이 유지된다는 것이다.
import copy
a = [[1,2],[3,4]]
b = copy.deepcopy(a)
a[1].append(5)
>>> a
[[1, 2], [3, 4, 5]]
>>> b
[[1, 2], [3, 4]]
nums개별 정수의 배열이 주어 지면 가능한 모든 순열을 반환합니다. 답은 어떤 순서로든 반환할 수 있습니다.
nums = [1,2,3]
result = []
prev=[]
def dfs(lst : list):
# 끝까지 탐색했을 경우 next에 빈 리스트가 들어옴
if len(lst) == 0:
result.append(prev[:]) # 복사본을 리스트에 추가
for e in lst:
next = lst[:]
next.remove(e)
prev.append(e)
dfs(next)
prev.pop()
return result
print(dfs(nums))
코드에서 result.append(prev)를 호출할 때, prev 리스트를 result 리스트에 추가하는 것이 아니라 result 리스트에 대한 참조인 prev 리스트를 추가하고 있다. 그래서 result 리스트의 각 요소는 모두 prev 리스트에 대한 참조로서 동일한 리스트이다. 따라서 prev 리스트가 변경될 때마다 result 리스트의 모든 요소도 함께 변경되는 것이다.
-> result.append(prev)의 결과값 [[], [], [], [], [], []]
해결하기 위해서는 result.append(prev) 대신에 result.append(prev[:])를 사용하여 prev 리스트의 복사본을 result 리스트에 추가하면 result 리스트에는 각각 별개의 리스트가 추가되어 원하는 결과를 얻을 수 있다.