ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [CS study] 2022.06.13
    CS 2022. 6. 13. 20:30

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

    mutable, immutable 객체에 대해 먼저 알아야 함

     

    immutable

    변경 불가능한 객체 (일반적인 자료형, int, string, tuple)

    # Int
    a = 1
    print(id(a)) #4346364144
    a += 1
    print(id(a)) #4346364176
    
    # String
    a = 'a'
    print(id(a)) #4304480688
    a += 'b'
    print(id(a)) #4304389296
    
    # Tuple
    a = (1,2)
    print(id(a)) #4343623232
    a += (3,)
    print(id(a)) #4343656000

    immutable 객체들은 값이 변경될때 객체가 변하는 것을 확인 할 수 있다.

    여기에 속한 객체들은 call by value의 속성을 띄고있다.

    # Int
    a = 1
    b = a
    print(a == b) # True
    b += 1
    print(a == b) # False
    
    # String
    a = 'a'
    b = a
    print(a == b) # True
    b += 'b'
    print(a == b) #False
    
    # Tuple
    a = (1,2)
    b = a
    print(a == b) # True
    b += (3,)
    print(a == b) # False

    immutable 객체들은 값이 변경되는것이 새로운 객체로 생성되는 것이다.

    변경이 일어날 때 기존 객체는 변하지 않는다.

     

    mutable

    변경 가능한 객체 (list, dict, set)

    # List
    a = [1,2]
    print(id(a)) #4335710848
    a.append(3)
    print(id(a)) #4335710848
    
    # Dict
    a = {1:'a'}
    print(id(a)) #4340089920
    a[2] = 'b'
    print(id(a)) #4340089920

    mutable 객체들은 값이 변경될 때 객체의 값이 변경되는것을 확인 할 수 있다.

    여기에 속한 객체들은 call by reference 속성을 가지고 있다.

    # List
    a= [1,2]
    b = a
    print(a == b) # True
    b.append(3)
    print(a == b) # True
    
    # Dict
    a = {1:'a'}
    b = a
    print(a == b) # True
    b[2] = 'b'
    print(a == b) # True

     

    얕은 복사 ([:], copy, copy.copy)

    • 변수를 복사했다고 생각했지만 실제로는 연결되어있는 것을 의미함
    • 변수를 복사 했지만 참조한 곳은 동일하기 때문에 같은 변수를 가리키고 있는 것

     

    예시

    # 얕은 복사
    arr1 = [1,2]
    arr2 = arr1
    arr1.append(3)
    print(arr1) # [1,2,3]
    print(arr2) # [1,2,3]
    • 변수를 복사했다고 생각했지만 사실 복사한 것은 메모리 주소만 복사한 것 실제 객체를 복사한 것이 아니다.
    • 그렇기 때문에 append를 이용해 값을 추가했을때 arr2에도 동일하게 적용이 되는 것이다.
    • 이렇게 복사를 했음에도 값을 변경하면 다른 변수에도 영향을 끼치도록 참조만 복사한 것을 얕은 복사라고 함
    • immutable 객체들은 얕은 복사를 하던 깊은 복사를 하던 사실 상관이 없음 값이 변경되면 무조건 참조가 변경되기 때문이다.
    • 즉 파이썬에서 얕은 복사 깊은 복사를 구분하고 학습해야하는 객체는 mutable 객체들이다.

     

    mutable 객체의 얕은 복사 방법 4가지

    1. '=' 대입 연산자를 이용한 얕은 복사 (위 예시와 같이 '='로 복사)

    2. [:] 슬라이싱을 이용한 얕은 복사 (눈속임)

    arr1 = [1,2,3,[4,5,6]]
    arr2 = arr1[:] # 복사
    
    # 메모리 주소가 다르다 깊은 복사 아닌가?
    print(arr1, id(arr1)) # [1, 2, 3, [4, 5, 6]] 4315672384
    print(arr2, id(arr2)) # [1, 2, 3, [4, 5, 6]] 4315704448
    
    # 값이 다름 깊은 복사 아닌가?
    arr2.append(32)
    print(arr1, id(arr1)) # [1, 2, 3, [4, 5, 6]] 4315672384
    print(arr2, id(arr2)) # [1, 2, 3, [4, 5, 6], 32] 4315704448
    
    # 리스트 안에 리스트 가르키고있는 주소가 같다 얕은 복사!
    print(arr1[3], id(arr1[3])) # [4, 5, 6] 4315099776
    print(arr2[3], id(arr2[3])) # [4, 5, 6] 4315099776
    
    # 리스트 안에 리스트에 값 추가
    arr1[3].append(64)
    print(arr1[3], id(arr1[3])) # [4, 5, 6, 64] 4315099776
    print(arr2[3], id(arr2[3])) # [4, 5, 6, 64] 4315099776
    
    # 전체 다시 확인 내부적으로 보면 앝은 복사이다!
    print(arr1, id(arr1)) # [1, 2, 3, [4, 5, 6, 64]] 4315672384
    print(arr2, id(arr2)) # [1, 2, 3, [4, 5, 6, 64], 32] 4315704448
    # 겉에 있는 리스트만 새롭게 객체를 추가했지만 사실 내부에 있는 리스트 요소는 하나의 [4,5,6] 리스트를 가리키고있음
    # 완전한 깊은 복사도 아니고 완전한 얕은 복사도 아니지만 이것 또한 얕은 복사로 구분한다.

    3. copy 메서드 이용한 얕은 복사 (객체에 제공)

    # 위 예시와 동일
    arr1 = [1,2,3,[4,5,6]]
    arr2 = arr1.copy() # 복사 copy 메서드 이용

    4. copy 모듈의 copy 함수를 이용한 얕은 복사 (딕셔너리 얕은 복사)

    import copy
    
    dict1 = {'a': 'rookie', 'b': [1,2,3]}
    dict2 = copy.copy(dict1)
    
    print(dict1, id(dict1)) # {'a': 'rookie', 'b': [1, 2, 3]} 433311033
    print(dict2, id(dict2)) # {'a': 'rookie', 'b': [1, 2, 3]} 4333110592
    
    # 새 key, value 추가
    dict2['c'] = '루키쉐'
    print(dict1, id(dict1)) # {'a': 'rookie', 'b': [1, 2, 3]} 4333110336
    print(dict2, id(dict2)) # {'a': 'rookie', 'b': [1, 2, 3], 'c': '루키쉐'} 4333110592
    
    # 딕셔너리 내부 리스트
    print(dict1['b'], id(dict1['b'])) # [1, 2, 3] 4333439808
    print(dict2['b'], id(dict2['b'])) # [1, 2, 3] 4333439808
    
    # 내부 리스트에 값 추가
    dict1['b'].append('no')
    print(dict1['b'], id(dict1['b'])) # [1, 2, 3, 'no'] 4333439808
    print(dict2['b'], id(dict2['b'])) # [1, 2, 3, 'no'] 4333439808
    
    # 전체 다시 확인
    print(dict1, id(dict1)) # {'a': 'rookie', 'b': [1, 2, 3, 'no']} 4333110336
    print(dict2, id(dict2)) # {'a': 'rookie', 'b': [1, 2, 3, 'no'], 'c': '루키쉐'} 4333110592
    # key, value를 추가했을때 보면 깊은 복사 같다.
    # 내부 리스트를 보면 주소가 동일한 것을 볼수 있고 내부 리스트에 값을 추가하면 둘다 추가되는 것을 확인 가능 얕은복사

     

    깊은 복사 (copy.deepcopy)

    • 깊은 복사를 사용하기 위해서는 copy 모듈의 deepcopy 함수를 사용해야함
    • 리스트 내부 리스트, 딕셔너리 내부 리스트 등 내부에 있는 객체를 모두 새롭게 만들어 주는 작업이다.
    • 모든 것을 다 새롭게 복사 독립적이게 됨
    import copy
    
    arr1 = [1,2,3,[4,5,6]]
    arr2 = copy.deepcopy(arr1) # 복사
    
    # 메모리 주소가 다르다 깊은 복사
    print(arr1, id(arr1)) # [1, 2, 3, [4, 5, 6]] 4367780608
    print(arr2, id(arr2)) # [1, 2, 3, [4, 5, 6]] 4367785152
    
    # 값이 다름 
    arr2.append(32)
    print(arr1, id(arr1)) # [1, 2, 3, [4, 5, 6]] 4367780608
    print(arr2, id(arr2)) # [1, 2, 3, [4, 5, 6], 32] 4367785152
    
    # 리스트 안에 리스트 들의 주소가 다르다!
    print(arr1[3], id(arr1[3])) # [4, 5, 6] 4367780288
    print(arr2[3], id(arr2[3])) # [4, 5, 6] 4367780736
    
    # 리스트 안에 리스트에 값 추가 주소가 달라서 arr2에 영향을 주지 않음
    arr1[3].append(64)
    print(arr1[3], id(arr1[3])) # [4, 5, 6, 64] 4367780288
    print(arr2[3], id(arr2[3])) # [4, 5, 6] 4367780736
    
    # 전체 다시 확인 독립적이다 깊은 복사
    print(arr1, id(arr1)) # [1, 2, 3, [4, 5, 6, 64]] 4367780608
    print(arr2, id(arr2)) # [1, 2, 3, [4, 5, 6], 32] 4367785152

    'CS' 카테고리의 다른 글

    [CS study] 2022.06.29  (0) 2022.06.29
    [CS study] 2022.06.17  (0) 2022.06.17
    [CS study] 2022.06.08  (0) 2022.06.08
    [CS study] 2022.06.01  (0) 2022.06.01
    [CS study] 2022.05.30  (0) 2022.05.31

    댓글

Designed by Tistory.