All Articles

파이썬 기억 더듬기 (7) - 얕은 복사(copy), 깊은 복사(deepcopy)

얕은 복사 (copy)

x = {'name': 'kim', 'age': 20}
y = x
print(x is y)
print(id(x), id(y))
True
140297523100416 140297523100416

따라서 y는 x와 완전히 같은 객체다.


z = {'name': 'kim', 'age': 20}
print(x is z)
print(id(x), id(z))
print(x == z)
False
140297523100416 140297524516864
True

x와 z는 값만 같고 별도로 생성된 다른 객체다. 따라서 x is z는 False고 id값은 서로 다르다. 하지만 값 자체는 같으므로 x == z 연산에서는 True를 리턴한다.

얕은 복사로 만든 객체는 다른 id값을 가졌더라도 리스트 내부 리스트나 리스트 내부 튜플의 id값은 같다.

tl1 = [[10], 1, 2, 3, 4, (0, 10)]
tl2 = tl1
tl3 = list(tl1)

여기서 tl1과 tl2는 id값까지 완전히 같은 객체지만 tl3은 tl1, tl2와 id값은 다르다. 따라서 tl1에 정수형 element를 추가하거나 제거해도 tl3에는 반영되지 않는다.

tl1.append(5)
tl1.remove(1)
print(tl1)
print(tl2)
print(tl3)
[[10], 2, 3, 4, (0, 10), 5]
[[10], 2, 3, 4, (0, 10), 5]
[[10], 1, 2, 3, 4, (0, 10)]

하지만 리스트 element를 수정하면 tl3에도 변경한 값이 반영된다. 얕은 복사라 리스트 element는 동일한 id를 가진 동일한 객체를 참조하기 때문이다.

print(id(tl1[0]), id(tl3[0]))
tl1[0] += [100, 1000]
print(tl1)
print(tl3)
140297524516928 140297524516928
[[10, 100, 1000], 2, 3, 4, (0, 10), 5]
[[10, 100, 1000], 1, 2, 3, 4, (0, 10)]

튜플 element 또한 같은 id 값을 갖는다.

print(id(tl1[4]), id(tl3[5]))
140297523151232
140297523151232

하지만 튜플은 불변객체이므로 튜플에 요소를 추가해주면 기존 튜플에 추가되는 것이 아니라 재할당된다. (=객체 새로 생성)

따라서 tl1[4] 아이디값이 달라진다.

tl1[4] += (20, 30)
print(tl1)
print(tl3)
[[10, 100, 1000], 2, 3, 4, (0, 10, 20, 30), 5]
[[10, 100, 1000], 1, 2, 3, 4, (0, 10)]
print(tl1[4])
print(tl3[5])
140297523132832
140297523151232

깊은 복사 (deepcopy)

이런 장바구니 클래스가 있을 때, 인스턴스를 생성하고 얕은 복사, 깊은 복사를 써서 인스턴스 2개를 더 만들어보겠다.

class Cart:
  def __init__(self, products=None):
    if products is None:
      self._products = []
    else:
      self._products = list(products)

  def add_product(self, prod_name):
    self._products.append(prod_name)

  def remove_product(self, prod_name):
    self._products.remove(prod_name)
import copy

cart1 = Cart(['Kiwi', 'Cookies', 'Peach', 'Icecream'])
cart2 = copy.copy(cart1)  # 얕복
cart3 = copy.deepcopy(cart1)  # 깊복

id값을 찍어보면 전부 다른 값이 리턴된다.

print(id(cart1), id(cart2), id(cart3))
140297524052368 140297524052896 140297524053808

하지만 cart1을 얕은 복사한 cart2 products id는 cart1과 같고, 깊은 복사한 cart3의 products id는 다르다.

print(id(cart1._products), id(cart2._products), id(cart3._products))
140297524141760 140297524141760 140297524062144

각 인스턴스가 가지고 있는 장바구니 품목(products)을 바꿔보자.

cart1.add_product('Water')
cart2.remove_product('Cookies')
print('cart1', cart1._products)
print('cart2', cart2._products)
print('cart3', cart3._products)
cart1 ['Kiwi', 'Peach', 'Icecream', 'Water']
cart2 ['Kiwi', 'Peach', 'Icecream', 'Water']
cart3 ['Kiwi', 'Cookies', 'Peach', 'Icecream']

예상대로 cart1과 cart2는 함께 변경되었고 cart3는 영향을 받지 않았다.