본문으로 건너뛰기

리스트 복사: copy와 이름 더 붙이기

파이썬 목차리스트와 튜플레슨 26 / 59

목차

copy()는 칸 값이 같은 새 리스트를 만들어 돌려주는 리스트 메서드입니다. b = a는 같은 리스트에 이름을 하나 더 붙일 뿐이지만, b = a.copy()로 만든 사본은 원래 리스트와 따로 바뀝니다.

주문을 고치기 전에 처음 주문을 남겨 두고 싶을 때가 있습니다. before = orders처럼 이름을 하나 더 붙이면 남은 것 같지만, 리스트는 하나뿐이라 고친 것이 before로도 보입니다(리스트와 이름 둘). 따로 남기려면 리스트를 하나 더 만들어야 합니다.

8단계 중 1단계. 이름 셋이 리스트에 어떻게 붙는지 보는 코드입니다.

1이름을 더 붙이기와 사본 만들기

그림의 코드입니다.

orders = ['커피', '라테']
today = orders
backup = orders.copy()
orders.append('쿠키')
print(today)
print(backup)
실행 결과
['커피', '라테', '쿠키']
['커피', '라테']

2번 줄 today = orders는 새 리스트 없이 today를 orders와 같은 리스트 ①에 붙입니다. 3번 줄 orders.copy()는 칸 값이 같은 새 리스트 ②를 만들어 돌려주고, backup은 ②에 붙습니다. 이렇게 같은 값을 하나 더 새로 만든 것을 사본(copy)이라고 합니다. 4번 줄은 ①에 '쿠키'를 더합니다. ①을 가리키는 today로 찍으면 '쿠키'가 보이고, ②를 가리키는 backup으로 찍으면 보이지 않습니다(규칙 3). copy()는 리스트를 제자리에서 바꾸기의 메서드들과 달리 리스트를 바꾸지 않고 새 리스트를 돌려줍니다. 그래서 backup = orders.copy()⁠처럼 돌려받은 리스트에 이름을 붙입니다.

2고치기 전 리스트 남기기

주문의 0번 칸을 고치기 전에, 처음 주문을 before로 남겨 두려 합니다. 무엇이 찍힐까요?

orders = ['커피', '라테']
before = orders
orders[0] = '녹차'
print(before)
실행 결과 보기
실행 결과
['녹차', '라테']

예상과 다르면 그곳이 배울 곳입니다. 2번 줄은 사본을 만들지 않고 같은 리스트에 이름 before를 하나 더 붙였습니다. 3번 줄이 그 리스트의 0번 칸을 바꾸니 before로 봐도 바뀌어 있습니다(규칙 3). 처음 주문을 남기려면 2번 줄을 before = orders.copy()⁠로 씁니다.

orders = ['커피', '라테']
before = orders.copy()
orders[0] = '녹차'
print(before)
print(orders)
실행 결과
['커피', '라테']
['녹차', '라테']

3같은 리스트인가, 칸 값이 같은가

orders = ['커피', '라테']
today = orders
backup = orders.copy()
print(today is orders)
print(backup is orders)
print(backup == orders)
실행 결과
True
False
True

is는 두 이름이 같은 리스트를 가리키는지, 곧 그림의 꼬리표가 같은지 묻습니다. == 연산자는 칸 값이 같은지 묻습니다. backup은 칸 값은 같지만 다른 리스트라서 is로는 False, == 연산자로는 True입니다.

예제

사본으로 내일 주문 만들기

오늘 주문의 사본으로 내일 주문을 만들고, 사본만 고칩니다.

orders = ['커피', '라테']
tomorrow = orders.copy()
tomorrow.remove('라테')
tomorrow.append('녹차')
print(orders)
print(tomorrow)
실행 결과
['커피', '라테']
['커피', '녹차']

3·4번 줄의 remove()·append()는 tomorrow가 가리키는 사본을 제자리에서 바꿉니다. orders의 리스트는 건드리지 않아 오늘 주문이 그대로 남습니다.

자주 틀리는 곳

틀린 생각: 리스트를 따로 두려면 b = a로 충분하다.

이름 b가 따로 생기니 리스트도 둘이 된 것처럼 보이지만, 이름만 둘이고 리스트는 하나입니다.

a = [1, 2]
b = a
c = a.copy()
a.append(3)
print(b)
print(c)
실행 결과
[1, 2, 3]
[1, 2]

a로 더한 3이 b로는 보이고, 사본 c에는 보이지 않습니다.

맞는 규칙: b = a는 같은 리스트에 이름을 하나 더 붙일 뿐입니다. 따로 바꿀 리스트가 필요하면 b = a.copy()로 새 리스트를 만듭니다(규칙 3).

틀린 생각: copy()도 리스트 메서드라서 None을 돌려준다.

append()·remove()가 None을 돌려주니 copy()도 그럴 것 같습니다. copy()는 리스트를 바꾸지 않는 대신 새 리스트를 돌려줍니다.

a = [1, 2]
b = a.copy()
print(b)
실행 결과
[1, 2]

맞는 규칙: append()·extend()·remove()는 리스트를 바꾸고 None을 돌려주지만, 바꾸지 않는 copy()는 새 리스트를 돌려줍니다. 그래서 b = a.copy()처럼 돌려받은 리스트에 이름을 붙입니다.

틀린 생각: 칸 값이 같은 리스트 둘은 같은 리스트다.

찍으면 똑같이 보이지만, 대괄호로 쓸 때마다 리스트가 새로 만들어집니다.

a = [1, 2]
b = [1, 2]
print(a == b)
print(a is b)
실행 결과
True
False

맞는 규칙: 대괄호로 쓸 때마다 새 리스트가 만들어집니다. 칸 값이 같은지는 == 연산자로, 한 리스트를 가리키는지는 is로 묻습니다(규칙 3).

확인 문제

1. 1절 코드의 4번 줄을 backup.append('쿠키')로 바꿨습니다. 무엇이 찍힐까요? 두 줄을 글자 그대로 쓰세요.

orders = ['커피', '라테']
today = orders
backup = orders.copy()
backup.append('쿠키')
print(today)
print(backup)
힌트

3번 줄 뒤 이름 셋이 각각 어느 리스트를 가리키는지 이름표로 그려 봅니다.

정답
실행 결과
['커피', '라테']
['커피', '라테', '쿠키']

4번 줄은 backup이 가리키는 사본 ②에만 '쿠키'를 더합니다. today는 orders와 같은 ①을 가리키므로 칸 둘 그대로입니다.

흔한 오답은 첫 줄도 ['커피', '라테', '쿠키']로 쓰는 것입니다. copy()도 같은 리스트에 이름만 하나 더 붙인다고 본 것입니다. copy()는 새 리스트를 만듭니다(규칙 3).

2. orders는 그대로 두고 today에만 '쿠키'를 더하려 합니다. 실행 결과가 아래 두 줄이 되도록 빈칸을 채우세요.

['커피', '라테']
['커피', '라테', '쿠키']
orders = ['커피', '라테']
today = ________
today.append('쿠키')
print(orders)
print(today)
힌트

3번 줄 뒤에 orders와 today가 각각 어떤 리스트를 가리켜야 하는지 이름표로 그려 봅니다.

정답

orders.copy()입니다.

orders = ['커피', '라테']
today = orders.copy()
today.append('쿠키')
print(orders)
print(today)
실행 결과
['커피', '라테']
['커피', '라테', '쿠키']

다르게 써도 되는 곳: orders[:]도 같은 결과입니다. 처음부터 끝까지 자른, 칸 값이 같은 새 리스트입니다. 빈칸에 ['커피', '라테']를 그대로 써도 같은 두 줄이 찍히지만, 1번 줄의 주문을 바꾸면 today는 따라 바뀌지 않습니다.

흔한 오답은 orders입니다. 그러면 두 줄 모두 ['커피', '라테', '쿠키']가 찍힙니다. b = a를 사본으로 본 것입니다(복사된다는 생각).

3. 어제 걸음 수를 고치기 전에 처음 기록을 saved에 남기려 했습니다. 그런데 처음 기록 [5000, 7000]이 아니라 [6000, 7000]이 찍힙니다. 몇 번 줄을 어떻게 고쳐야 할까요?

steps = [5000, 7000]
saved = steps
steps[0] = 6000
print(saved)
실행 결과
[6000, 7000]
힌트

4번 줄에서 saved와 steps가 가리키는 리스트가 몇 개인지 이름표로 그려 봅니다.

정답

2번 줄을 saved = steps.copy()로 고칩니다.

steps = [5000, 7000]
saved = steps.copy()
steps[0] = 6000
print(saved)
실행 결과
[5000, 7000]

고치기 전 2번 줄은 saved를 steps와 같은 리스트에 붙였을 뿐이라, 3번 줄이 바꾼 0번 칸이 saved로도 보였습니다. 2번 줄이 그때의 기록을 따로 담는다고 본 것입니다(복사된다는 생각).

다르게 고쳐도 되는 곳: 2번 줄을 saved = steps[:]로 써도 [5000, 7000]이 찍힙니다. steps[:]는 처음부터 끝까지 자른 새 리스트입니다. 3번 줄을 steps = [6000, 7000]처럼 새 리스트에 다시 붙여도 saved는 처음 리스트에 남지만, 칸 하나를 고칠 때마다 리스트를 통째로 다시 써야 합니다.

아래 코드 4번 줄 다음에 backup.append('녹차')를 한 줄 더하면 두 print가 무엇을 찍는지 봅니다.

처음 펼 때 약 6MB를 받습니다.컴퓨터에서 해 보기

끝까지 내려오면 저절로 표시됩니다. 이 기기에만 저장합니다.

파이썬 목차로