파이썬 리스트 복사 어떻게 할까?

Photo by Zdeněk Macháček on Unsplash

오늘은 상당히 기초적이면서도 쉽게 실수 할 수 있는 부분을 다뤄보고자 한다. 바로 나를 포함한 초보자들이 실수할 수 있는 파이썬 리스트를 다른 리스트에 복사하는 방법이다.

그냥 새로운 변수에 정의해주면 되는거 아냐 🤔❓

>>> list1 = [1, 2, 3, 4]
>>> list2 = list1

>>> print(list1)
[1, 2, 3, 4]
>>> print(list2)
[1, 2, 3, 4]

위 예제의 결과만 보면 파이썬이 워낙 간편하다보니 별 의심없이 잘 복사됐다고 생각할 수 있다. 하지만 정확히 말하면 list2list1의 메모리 주소값을 복사한 것이다. 즉, 같은 메모리를 참조하기 때문에 list1이 수정되면 list2도 동시에 수정된다. 하지만 이것을 간과하고 열심히 코드를 짜다보면 나중에 깊은 오류의 구렁텅이에 빠질 수 있다. 혹시나 그런 오류에 빠진 사람들이 이 글을 보고 도움이 되었으면 좋겠다 :)

아니 이게 왜 안돼 🐥❓

>>> list1 = [1, 2, 3, 4]
>>> list2 = list1

>>> print('list1 :', list1, '/ id :', id(list1))
list1 : [1, 2, 3, 4] / id : 4552076160
>>> print('list2 :', list2, '/ id :', id(list2))
list2 : [1, 2, 3, 4] / id : 4552076160

>>> list1[0] = 11

>>> print('list1 :', list1)
list1 : [11, 2, 3, 4] 
>>> print('list2 :', list2)
list2 : [11, 2, 3, 4]

파이썬 객체의 메모리 주소를 확인하는 id() 함수를 이용해 list1list2의 주소값을 확인해보면 두 리스트 모두 4552076160로 같은 주소값을 가지는 것을 확인할 수 있다. 엄밀히 말해서 list2는 list1의 분신과 같은 존재이고, 일반적으로 우리가 원하는 복사는 이뤄지지 않은 것이다.

그럼 어떻게 해야 하는데 😲❓

앞서 살펴본 문제점과 같이 우리가 원하는 복사가 이뤄지지 않았던 이유는 list1list2 두 변수가 같은 메모리를 참조하기 때문이다. 결국 우리가 원하는 복사가 이뤄지기 위해서는 list1list2이 서로 다른 메모리를 참조하도록 해주면 된다. 파이썬 리스트를 다른 리스트에 복사하는 방법은 4가지가 있다.

1) 슬라이싱

>>> list1 = [1, 2, 3, 4]
>>> list2 = list1[:]

시작점과 끝점을 생략한 슬라이싱은 리스트의 모든 요소를 뜻한다. 슬라이싱을 통해 변수를 정의하면 파이썬은 새로운 객체를 만든다.

2) list() 함수

>>> list1 = [1, 2, 3, 4]
>>> list2 = list(list1)

파이썬 내장함수 중에는 iterable한 객체를 리스트 객체로 변환해주는 list() 함수가 있다. 이 함수를 이용해 복사하고자 하는 리스트 객체를 다시 재선언 한다.

3) copy() 메소드

>>> list1 = [1, 2, 3, 4]
>>> list2 = list1.copy()

Python3 부터 리스트를 다른 리스트에 복사하는 기능인 copy() 메소드가 추가되었다. 가독성을 위한 코드라면 이 방법을 사용하는 것을 권장한다.

4) 리스트 연산

>>> list1 = [1, 2, 3, 4]
>>> list2 = [] + list1

리스트 덧셈연산을 이용해 새로운 빈 리스트에 list1의 요소들을 더한다. 새로운 리스트 객체에 list1의 요소들이 더해져 결과적으로 우리가 원하는 복사가 이뤄진다.

검증

>>> def copy_element(method):
        list1 = [1, 2, 3, 4]
        if method == 1:
            list2 = list1[:]
        elif method == 2:
            list2 = list(list1)
        elif method == 3:
            list2 = list1.copy()
        elif method == 4:
            list2 = [] + list1

        print(f'방법 {method}')
        print('list1 :', list1, '/ id :', id(list1))
        print('list2 :', list2, '/ id :', id(list2))

        list1[0] = 11

        print('list1 :', list1)
        print('list2 :', list2)
        print('')

>>> [copy_element(method) for method in range(1, 5)]
방법 1
list1 : [1, 2, 3, 4] / id : 4552075648
list2 : [1, 2, 3, 4] / id : 4552076096
list1 : [11, 2, 3, 4]
list2 : [1, 2, 3, 4]

방법 2
list1 : [1, 2, 3, 4] / id : 4552076096
list2 : [1, 2, 3, 4] / id : 4552164288
list1 : [11, 2, 3, 4]
list2 : [1, 2, 3, 4]

방법 3
list1 : [1, 2, 3, 4] / id : 4552164288
list2 : [1, 2, 3, 4] / id : 4552076096
list1 : [11, 2, 3, 4]
list2 : [1, 2, 3, 4]

방법 4
list1 : [1, 2, 3, 4] / id : 4552076096
list2 : [1, 2, 3, 4] / id : 4552075648
list1 : [11, 2, 3, 4]
list2 : [1, 2, 3, 4]

각자의 방법 모두 서로 다른 리스트 객체에 값이 할당되며, list1의 요소에 수정이 일어나도 list2 요소에 영향을 끼치지 않는 독립적인 개체임을 확인할 수 있다.

참고

Python: How to Copy a List? (The Idiomatic Way)

댓글 남기기