[파이썬 알고리즘] 객체 복사

신현식·2023년 6월 12일
0

파이썬알고리즘

목록 보기
4/4
post-thumbnail

객체복사

파이썬의 중요한 특징 중 하나는 모든 것이 객체라는 점이다. 심지어 문자, 숫자까지도 모두 객체다. 그러다 보니 별도로 값을 복사하지 않는 이상 번수에 값을 할당하는 모든 행위는 값 객체에 대한 참조가 된다.
이말은 참조가 가리키는 원래의 값을 변경하면 모든 참조, 즉 모든 변수의 값 또한 함께 변경된다는 말이다.

얕은 복사(shallow copy)

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]]

깊은 복사(deep copy)

깊은 복사는 내부에 객체들까지 모두 새롭게 copy 되는 것이다. 즉, 중첩된 mutable 변수에 대해서도 완전히 독립성이 유지된다는 것이다.

  • copy.deepcopy메소드
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]]

리트코드 46. Permutations

nums개별 정수의 배열이 주어 지면 가능한 모든 순열을 반환합니다. 답은 어떤 순서로든 반환할 수 있습니다.

  • Input: nums = [1,2,3]
    Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
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 리스트에는 각각 별개의 리스트가 추가되어 원하는 결과를 얻을 수 있다.

profile
전공 소개

0개의 댓글