리스트, 리스트의 복사
파이썬 3에서 리스트를 사용하는 방법과 복사 개념에 대해 설명합니다. 얕은 복사와 얕은복사2, 깊은 복사 에 대해 설명하며 메모리 주소의 차이를 예제로 보여줍니다.
![리스트, 리스트의 복사](/content/images/size/w1200/2022/04/python3-------.001.png)
리스트
기본 문법
>>> squares = [1, 4, 9, 16, 25]
>>> squares
[1, 4, 9, 16, 25]
인덱스 접근
해당 문법은 문자열에서 존재하는 인덱스 접근 방법 문법과 같은 문법을 사용합니다
>>> squares[0] # 인덱스에서 요소 하나를 꺼냅니다
1
>>> squares[-1]
25
>>> squares[-3:] # 리스트를 잘라 새로운 시퀀스를 만들어 리턴합니다.
[9, 16, 25]
>>> squares = [1, 4, 9, 16, 25]
>>> squares = [1, 4, 9, 16, 25] # 자르기 시작할 위치(이상) : 자르기 종료할 위치(미만)
[1, 4]
>>> squares[:2]
[1, 4]
>>> squares[2:]
[9, 16, 25]
>>> squares[-2:] # 음수 인덱스는 뒤부터 접근
[16, 25]
>>> squares[23] # 너무 큰 인덱스는 에러
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
>>> squares[2:23] # 범위를 벗어나는 자르기는 허용
[9, 16, 25]
>>> squares[23:] # 슬라이스 할 때는 인덱스가 범위를 벗어나더라도 관대하게 처리된다
[]
리스트의 복사
복사에는 얕은 복사, 얕은 복사 2, 깊은 복사 가 있습니다
얕은 복사
# 얕은 복사
source = [[1, 2, 3], [[4, 5, 6], [7, 8, 9]], 10, 11, 12]
# 아래와 같이 입력하면 source 가 얕은 복사로 동작하여 target 에 입력된다
target = source
객체의 값이 아니라 메모리의 위치를 복사하여 대입합니다.
따라서 대입된 새로운 곳에서 값을 변경하면, 그 값의 변경이 기존의 객체에도 영향을 주게됩니다.
>>> array1 = [1, 2, 3, 4, 5]
>>> array2 = array1
>>> array2
[1, 2, 3, 4, 5]
>>> array2[0] = 6
>>> print(f'''
... array1: {array1} // memory_id: {id(array1)}
... array2: {array2} // memory_id: {id(array2)}
... ''')
array1: [6, 2, 3, 4, 5] // memory_id: 4546189248
array2: [6, 2, 3, 4, 5] // memory_id: 4546189248
예제를 잘 보면 값의 변경도, 메모리의 위치도 같은 것을 볼 수 있습니다
얕은 복사 2
해당 복사는 깊은 복사와 얕은 복사가 어떠한 조건에 의해 수행됩니다
이를 지원하는 문법은 다음과 같습니다
# 얕은 복사(1 depth만 깊은 복사 수행)
import copy
source = [[1, 2, 3], [[4, 5, 6], [7, 8, 9]], 10, 11, 12]
# 아래와 같이 입력하면 source 가 얕은 복사2 로 동작하여 target 에 입력된다.
target1 = source[:] # 다른 슬라이스 키워드를 사용하더라도 마찬가지로 동작한다
target2 = copy.copy(source)
위 문법을 사용하여 복사를 수행할 경우 모두 아래와 같이 복사가 수행됩니다.
1 Depth 까지는 깊은 복사가 수행되며 2 Depth 이상 부터는 얕은 복사가 수행됩니다
>>> array1 = [[1, 2, 3], [[4, 5, 6], [7, 8, 9]], 10, 11, 12]
>>> array2 = array1[:]
>>> print(f'''
... array1: {array1} // memory_id: {id(array1)}
... array2: {array2} // memory_id: {id(array2)}
... ''')
array1: [[1, 2, 3], [[4, 5, 6], [7, 8, 9]], 10, 11, 12] // memory_id: 4546190592
array2: [[1, 2, 3], [[4, 5, 6], [7, 8, 9]], 10, 11, 12] // memory_id: 4546190208
1 Depth 까지는 깊은 복사가 수행되어 메모리의 id 값이 다른 것을 볼 수 있습니다.
하지만 2 Depth 이상 부터 메모리 값을 찍어보면 얕은 복사가 수행되었음을 알 수 있습니다
다음은 2 Depth 의 리스트 입니다.
>>> print(f'''
array1[1]: {array1[1]} // memory_id: {id(array1[1])}
array2[1]: {array2[1]} // memory_id: {id(array2[1])}
''')
array1[1]: [[4, 5, 6], [7, 8, 9]] // memory_id: 4546189184
array2[1]: [[4, 5, 6], [7, 8, 9]] // memory_id: 4546189184
다음은 3 Depth 의 메모리 참조 확인입니다. 역시 얕은 복사가 수행되었습니다.
>>> print(f'''
array1[1][0]: {array1[1][0]} // memory_id: {id(array1[1][0])}
array2[1][0]: {array2[1][0]} // memory_id: {id(array2[1][0])}
''')
array1[1][0]: [4, 5, 6] // memory_id: 4546190336
array2[1][0]: [4, 5, 6] // memory_id: 4546190336
깊은 복사
모든 Depth 에서 깊은 복사를 수행하는 방법도 존재합니다
아래의 문법을 통해 수행할 수 있습니다
# 깊은 복사
import copy
source = [[1, 2, 3], [[4, 5, 6], [7, 8, 9]], 10, 11, 12]
target3 = copy.deepcopy(source)
정말 깊은 복사가 수행되었는지 확인해봅시다
>>> print(f'''Depth 확인
target1.id={id(target1)} // source.id={id(source)}
target1[1].id={id(target1[1])} // source[1].id={id(source[1])}
target1[1][0].id={id(target1[1][0])} // source[1][0].id={id(source[1][0])}
''')
1 Depth 확인
target1.id=4546190272 // source.id=4546190848 # 1 Depth
target1[1].id=4546189376 // source[1].id=4546190720 # 2 Depth
target1[1][0].id=4546190592 // source[1][0].id=4546190784 # 3 Depth
메모리의 위치가 모두 다른 것을 확인 할 수 있습니다.