본문으로 건너뛰기

리스트와 이름 둘

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

목차

리스트(list)는 값 여럿을 차례로 담은 값입니다. 자리 번호로 값을 꺼내고 append()로 끝에 값을 더하며, 이름 둘이 한 리스트를 가리키면 한쪽 이름으로 바꾼 것이 다른 쪽 이름으로도 보입니다.

주문이 몇 개일지 미리 모르면 주문마다 이름을 지어 둘 수 없습니다. 리스트 하나에 주문을 모두 담고 이름 하나를 붙이면 됩니다. 그런데 리스트는 수나 문자열과 달리 만든 뒤에도 바뀌는 값입니다. 이것을 모르면 이름 둘이 한 리스트를 가리킬 때, 손대지 않은 이름의 리스트가 저절로 바뀐 것처럼 보입니다. 이 레슨에서는 이름표 규칙(규칙 3)을 리스트에서 다시 봅니다.

이 단원(리스트와 튜플)을 이미 안다면 단원 연습 문제부터 풀고 틀린 레슨만 보세요.

6단계 중 1단계. 리스트를 이름 orders와 today로 다루는 코드입니다.

1리스트를 만들고 꺼내기

대괄호 안에 값을 쉼표로 나열하면 리스트가 하나 만들어집니다.

orders = ['커피', '라테', '녹차']
print(orders)
print(orders[0])
print(orders[-1])
print(len(orders))
실행 결과
['커피', '라테', '녹차']
커피
녹차
3

리스트를 통째로 찍으면 대괄호와 쉼표, 문자열의 따옴표까지 찍힙니다. 쉼표 뒤에는 빈칸 하나, 문자열은 큰따옴표로 썼어도 보통 작은따옴표로 찍힙니다. 칸 하나를 꺼내 찍으면 따옴표 없이 그 값만 찍힙니다. 결과를 쓸 때도 이 꼴 그대로 씁니다. 리스트의 칸에도 문자열의 글자처럼 0부터 자리 번호가 붙고, 꺼내는 법도 인덱스와 자르기와 같습니다. orders[-1]은 마지막 칸이고, orders[0:2]처럼 자르면 앞 두 칸을 담은 새 리스트가 만들어집니다. len()은 칸 수를 셉니다. 리스트에 든 값을 자료에 따라 요소(element)나 항목(item)이라고도 부릅니다. 칸이 하나도 없는 빈 리스트는 []로 씁니다.

2이름 둘이 한 리스트를 가리킨다

그림의 코드입니다. append()는 리스트 끝에 값 하나를 더하는 메서드입니다.

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

2번 줄 today = orders는 오른쪽 orders를 계산해 orders가 가리키는 리스트를 얻고, 이름 today를 바로 그 리스트에 붙입니다(규칙 2). 새 리스트는 만들지 않습니다. 그래서 이름은 둘이고 리스트는 하나입니다(규칙 3). 3번 줄은 today가 가리키는 리스트 끝에 '쿠키'를 더합니다. 리스트를 새로 만들지 않고 그 리스트를 제자리에서(in place) 바꿉니다. 4번 줄의 orders도 같은 리스트를 가리키므로 칸 셋이 다 찍힙니다. 이름표 다시 붙이기에서 미뤄 둔 장면이 이것입니다.

이름이 값을 가리키는 것을 자료에 따라 참조(reference)라고도 부르고, today처럼 한 값에 붙은 또 하나의 이름을 별칭(alias)이라고 합니다.

그림은 리스트를 이름 칸 밖에 따로 그리고 머리에 꼬리표 ①을 붙입니다. 꼬리표가 같으면 같은 리스트입니다. 칸 안의 '커피' 같은 값은 칸에 바로 적었습니다. 정확히는 칸도 값을 가리키지만, 수와 문자열은 바꿀 수 없는 값이라 이렇게 줄여 적어도 결과가 같습니다.

3다시 붙이면 그 이름만 옮겨 간다

2절 코드의 3번 줄을 today = ['쿠키']로 바꾸고, 끝에 print(today)를 더했습니다. 무엇이 찍힐까요?

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

예상과 다르면 그곳이 배울 곳입니다. 3번 줄은 리스트를 바꾸지 않습니다. 새 리스트 ['쿠키']를 만들고 이름 today만 그 리스트로 옮겨 붙입니다. orders는 처음 리스트에 그대로 붙어 있어 ['커피', '라테']를 찍습니다(규칙 3).

3번 줄하는 일orders로 보면
today.append('쿠키')같은 리스트를 제자리에서 바꾼다바뀌어 있다
today = ['쿠키']새 리스트에 today만 다시 붙인다그대로다

예제

마지막 주문 꺼내기

마지막 주문을 꺼내려고 칸 수 len(orders)를 자리 번호로 썼습니다.

orders = ['커피', '라테', '녹차']
print(orders[len(orders)])
실행 결과
Traceback (most recent call last):  File "main.py", line 2, in <module>    print(orders[len(orders)])          ~~~~~~^^^^^^^^^^^^^IndexError: list index out of range

맨 아래 줄은 '리스트의 자리 번호가 범위를 벗어났다'는 뜻입니다. 칸 수는 3이지만 번호는 0부터라 0, 1, 2뿐입니다. 마지막 칸은 orders[len(orders) - 1], 더 짧게는 orders[-1]로 꺼냅니다.

자주 틀리는 곳

틀린 생각: b = a를 하면 b는 a가 가리키는 리스트의 사본을 받는다.

수에서는 b = a 뒤에 a를 다시 붙여도 b가 그대로였습니다. 그래서 b가 사본을 받은 것처럼 보이지만, b = a는 같은 값에 이름을 하나 더 붙일 뿐입니다.

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

b로 더한 3이 a로 봐도 보입니다.

맞는 규칙: b = a는 새 리스트를 만들지 않고 a가 가리키는 리스트에 이름 b를 하나 더 붙입니다. 그래서 한 이름으로 리스트를 바꾸면 다른 이름으로도 보입니다(규칙 3).

틀린 생각: 두 이름 가운데 하나를 새 리스트에 다시 붙이면 다른 이름도 따라 옮겨 간다.

한쪽 이름으로 바꾼 것이 다른 쪽에도 보이니, 다시 붙이기도 함께 일어날 것 같습니다. 그러나 다시 붙이기는 리스트를 바꾸지 않습니다.

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

3번 줄은 새 리스트 [3]을 만들고 b만 그 리스트로 옮겨 붙입니다. a가 가리키는 [1, 2]는 아무도 바꾸지 않았습니다.

맞는 규칙: 다시 붙이기는 그 이름 하나만 새 값으로 옮길 뿐 리스트를 바꾸지 않습니다. 다른 이름으로도 보이는 것은 append()처럼 리스트를 제자리에서 바꾼 때뿐입니다(규칙 3).

확인 문제

1. 2절 코드의 4번 줄을 print(orders[-1])로 바꿨습니다. 무엇이 찍힐까요?

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

2번 줄 뒤 today와 orders가 각각 어느 리스트를 가리키는지 먼저 그려 봅니다.

정답
실행 결과
쿠키

2번 줄에서 today가 orders와 같은 리스트에 붙고, 3번 줄이 그 리스트 끝에 '쿠키'를 더합니다. orders로 봐도 마지막 칸은 '쿠키'입니다.

흔한 오답은 라테입니다. today가 사본을 받아 orders는 그대로라고 본 것입니다(복사된다는 생각). today = orders는 같은 리스트에 이름을 하나 더 붙입니다(규칙 3).

2. 실행 결과가 아래 두 줄이 되도록 ㉠~㉤을 위에서 아래로 놓으세요.

['커피', '라테', '쿠키']
['커피', '라테', '쿠키']
  • ㉠ print(orders)
  • ㉡ today.append('쿠키')
  • ㉢ today = orders
  • ㉣ orders = ['커피', '라테']
  • ㉤ print(today)
힌트

놓아 본 차례대로 줄마다 이름표를 그리고, 리스트가 몇 개 생기는지 셉니다.

놓은 뒤 물음

놓은 차례에서 ㉢이 ㉡보다 위에 오는 까닭은 무엇일까요?

  • ① ㉢으로 today가 orders와 같은 리스트에 붙어 있어야, ㉡이 그 리스트에 '쿠키'를 더하기 때문
  • ② ㉢이 orders의 사본을 만들어 두어야, ㉡이 '쿠키'를 더할 리스트가 생기기 때문
  • ③ ㉢이 이름 today를 이름 orders에 묶어 두어야, today에 일어난 일이 orders에도 일어나기 때문
정답

㉣ ㉢ ㉡ ㉠ ㉤ 차례이고, 까닭은 ①입니다.

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

㉠과 ㉤은 같은 리스트를 찍으므로 ㉣ ㉢ ㉡ ㉤ ㉠으로 바꿔 놓아도 맞습니다.

  • ② 사본은 생기지 않습니다. ㉢은 같은 리스트에 이름을 하나 더 붙일 뿐이고, 사본이었다면 ㉠은 ['커피', '라테']를 찍습니다(복사된다는 생각).
  • ③ 이름끼리 묶이지 않습니다. 두 이름이 한 리스트를 가리킬 뿐이라, today를 새 리스트에 다시 붙이면 orders는 따라가지 않습니다(규칙 3).

3. 장보기 목록을 두 이름으로 다루는 코드입니다. 무엇이 찍힐까요? 두 줄을 글자 그대로 쓰세요.

cart = ['우유', '빵']
mine = cart
cart = ['사과']
mine.append('달걀')
print(cart)
print(mine)
힌트

줄마다 이름표를 다시 그려, 5번 줄과 6번 줄에 왔을 때 cart와 mine이 각각 어느 리스트를 가리키는지 봅니다.

정답
실행 결과
['사과']
['우유', '빵', '달걀']

2번 줄에서 mine이 cart와 같은 리스트 ['우유', '빵']에 붙습니다. 3번 줄은 cart만 새 리스트 ['사과']로 옮겨 붙이고, mine은 처음 리스트에 그대로 붙어 있습니다. 4번 줄은 mine이 가리키는 처음 리스트에 '달걀'을 더합니다.

흔한 오답은 두 줄 모두 ['사과', '달걀']입니다. 3번 줄에서 mine도 cart를 따라 새 리스트로 옮겨 간다고 본 것입니다. 다시 붙이기는 그 이름 하나만 옮깁니다(규칙 3).

아래 코드 3번 줄 today.append('쿠키')를 today = today + ['쿠키']⁠로 바꾸면 4번 줄이 무엇을 찍는지 봅니다.

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

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

파이썬 목차로