파이썬프로그래밍기초 15강 - 2차원 리스트
리스트 안에 리스트를 저장하는 2차원 리스트의 구조를 이해하고 행과 열 인덱스로 원하는 원소에 접근하는 방법을 학습한다. 정적으로 값을 지정하거나 반복문으로 동적 생성하는 방법, 중첩 반복문으로 모든 원소를 순회하는 방법을 거리표 예제로 익힌다.
제1장 리스트와 차원의 개념
1. 리스트의 기본 성격
리스트는 여러 개의 데이터 값을 순서대로 묶어 저장하는 자료구조이다. 각 원소는 위치를 나타내는 인덱스를 가지며, 첫 번째 원소의 인덱스는 0이다. 파이썬 리스트에는 숫자나 문자열뿐 아니라 다른 리스트처럼 서로 다른 자료형의 값도 저장할 수 있다.
hei_list = [1, 5, 14, 20, 31]
위 리스트는 값들이 한 방향으로 나열된 1차원 구조이다. hei_list[0]은 1, hei_list[2]는 14를 가리킨다. 리스트의 한 원소에 또 다른 리스트를 넣을 수 있다는 성질이 2차원 리스트의 출발점이다.
2. 2차원 리스트란
2차원 리스트는 리스트 내부에 여러 리스트가 들어 있는, 즉 리스트의 리스트이다. 내부 리스트 하나를 표의 한 행으로 보면 전체 구조를 행과 열로 이루어진 표처럼 다룰 수 있다. 여러 도시 사이의 거리를 나타내는 거리표, 학생별 과목 점수, 영상의 픽셀 값처럼 두 기준으로 배열되는 데이터에 적합하다.
차원의 핵심
1차원 리스트는 하나의 인덱스로 원소를 찾지만, 2차원 리스트는 바깥 리스트에서 행을 고른 뒤 내부 리스트에서 열을 고르므로 두 개의 인덱스를 사용한다.
제2장 2차원 리스트의 구조
1. 거리표로 이해하는 행과 열
강의록의 거리표는 서울, 대전, 광주, 대구, 울산, 전주, 순천, 부산의 여덟 도시 사이 거리를 8개의 행과 8개의 열로 나타낸다. 각 행은 출발 도시, 각 열은 도착 도시를 의미한다. 예를 들어 서울 행에는 서울에서 각 도시까지의 거리 [0, 151, 290, 282, 396, 210, 320, 390]이 들어 있다.
같은 도시에서 같은 도시로 이동하는 거리는 0이므로 표의 왼쪽 위에서 오른쪽 아래로 이어지는 주대각선 원소는 모두 0이다. 도시 간 거리가 왕복 방향에서 같다고 가정하면 서울-대전과 대전-서울처럼 행과 열을 맞바꾼 위치의 값도 같다.
| 행 인덱스 | 출발 도시 | 해당 행의 일부 거리 |
|---|---|---|
| 0 | 서울 | 서울 0, 대전 151, 광주 290, 부산 390 |
| 1 | 대전 | 서울 151, 대전 0, 광주 178, 부산 260 |
| 2 | 광주 | 서울 290, 대전 178, 광주 0, 부산 267 |
| 7 | 부산 | 서울 390, 대전 260, 광주 267, 부산 0 |
2. 바깥 리스트와 내부 리스트
변수 distance가 전체 거리표를 참조한다면 distance[0]은 서울을 출발지로 하는 첫 번째 행 전체를 가리킨다. distance[1]은 대전 행, distance[7]은 부산 행을 가리킨다. 각 행 역시 독립된 리스트이므로 다시 인덱스를 사용할 수 있다.
distance[0] # 서울 출발 거리 행 전체
distance[0][1] # 서울에서 대전까지의 거리 151
distance[2][7] # 광주에서 부산까지의 거리 267
distance[7][7] # 부산에서 부산까지의 거리 0
첫 번째 인덱스는 행, 두 번째 인덱스는 열이다. 따라서 distance[row][column]은 먼저 distance[row]로 내부 리스트를 고르고, 그 리스트의 column 위치에 있는 값을 선택하는 과정으로 해석할 수 있다.
제3장 2차원 리스트 접근과 변경
1. 행 단위 접근
2차원 리스트에서 인덱스 하나만 사용하면 원소 하나가 아니라 내부 리스트 하나를 얻는다. distance[0]의 자료형은 리스트이며, 이 행의 길이는 열의 개수와 같다. 특정 행의 합계나 최댓값을 구할 때 내부 리스트를 그대로 함수에 전달할 수 있다.
seoul_row = distance[0]
print(seoul_row)
print(len(seoul_row))
2. 개별 원소 접근
개별 원소를 읽으려면 행과 열 인덱스를 연속해서 사용한다. 인덱스는 모두 0부터 시작하므로 사람이 표에서 말하는 첫째 행과 첫째 열은 코드에서 각각 0번 인덱스이다. 행 또는 열 범위를 벗어나면 IndexError가 발생하므로 실제 크기를 확인해야 한다.
value = distance[3][4]
distance[3][4] = 100
리스트는 변경 가능한 자료구조이므로 위와 같이 특정 위치의 값을 새 값으로 바꿀 수 있다. 다만 거리표처럼 서로 반대 방향의 값이 같아야 하는 데이터라면 distance[3][4]를 바꿀 때 대칭 위치인 distance[4][3]도 함께 갱신해야 일관성이 유지된다.
3. 행과 열 크기 확인
행의 개수는 바깥 리스트의 길이인 len(distance)로 확인한다. 모든 행의 열 개수가 같다면 첫 행의 길이 len(distance[0])를 열 개수로 사용할 수 있다. 각 행의 길이가 서로 다른 비정형 2차원 리스트도 가능하므로 일반적인 코드에서는 현재 처리하는 행의 길이를 확인하는 편이 안전하다.
직사각형만 가능한 것은 아니다
파이썬의 2차원 리스트는 내부 리스트들의 길이가 반드시 같을 필요가 없다. 그러나 표나 행렬로 사용할 때에는 각 행의 열 개수를 같게 유지해야 인덱스와 반복 처리가 단순해진다.
제4장 2차원 리스트 생성
1. 값이 정해진 정적 생성
저장할 값이 미리 정해져 있다면 내부 리스트들을 직접 나열하여 2차원 리스트를 만든다. list([원소 시퀀스1], [원소 시퀀스2], ...)와 같은 방식이 아니라, 바깥 대괄호 안에 각 행이 될 리스트를 쉼표로 구분해 작성하는 형태가 일반적이다.
distance = [
[0, 151, 290, 282, 396, 210, 320, 390],
[151, 0, 178, 148, 259, 90, 235, 260],
[290, 178, 0, 219, 327, 105, 93, 267],
[282, 148, 219, 0, 87, 198, 218, 130]
]
각 내부 리스트는 하나의 행을 나타내며, 바깥 리스트에서 행의 순서가 결정된다. 표의 행과 열 의미를 코드의 인덱스와 일치시키면 데이터 해석이 쉬워진다.
2. 반복문을 이용한 동적 생성
행과 열의 크기가 실행 중에 결정되거나 값이 계산·입력·난수로 만들어진다면 반복문으로 동적 생성한다. 강의록의 예제는 빈 2차원 리스트를 만들고, 각 행마다 빈 row를 생성한 뒤 0부터 99 사이의 난수를 열 개수만큼 추가한다. 완성된 행을 바깥 리스트에 추가하는 순서가 중요하다.
import random as rd
nRows = 3
nColumns = 4
distance = []
for i in range(nRows):
row = []
for j in range(nColumns):
row.append(rd.randint(0, 99))
distance.append(row)
print(distance)
바깥 반복문은 행을 만들고, 안쪽 반복문은 현재 행의 열 원소를 만든다. 안쪽 반복이 끝난 뒤에만 완성된 row를 distance에 추가해야 원하는 행×열 구조가 만들어진다.
3. 같은 내부 리스트를 공유하는 실수
2차원 리스트를 만들 때 하나의 내부 리스트 객체를 여러 번 참조하게 만들면 한 행을 수정했을 때 다른 행도 함께 바뀔 수 있다. 각 행이 독립적이어야 한다면 반복할 때마다 새 리스트를 생성해야 한다. 강의록의 동적 생성 코드가 바깥 반복문 안에서 row = []를 실행하는 이유도 각 행을 별개의 리스트로 만들기 위해서이다.
제5장 2차원 리스트 순회
1. 값 중심 중첩 반복
2차원 리스트의 모든 원소를 순서대로 처리하려면 두 개의 반복 구조를 중첩한다. 바깥 반복문은 각 행을, 안쪽 반복문은 그 행에 들어 있는 각 값을 순회한다. 강의록의 거리표에서는 바깥 변수 dep가 출발 도시의 행을, 안쪽 변수 des가 각 도착 도시의 거리값을 차례로 받는다.
for dep in distance:
for des in dep:
print(des, end=" ")
print()
print(des, end=" ")는 같은 행의 값을 한 줄에 이어 출력한다. 안쪽 반복이 끝난 뒤의 print()는 줄을 바꾸므로 원래의 표 구조와 비슷한 형태로 출력할 수 있다.
2. 인덱스 중심 중첩 반복
값뿐 아니라 행과 열 위치가 필요하면 range()와 len()으로 인덱스를 순회한다. 이 방식은 특정 위치를 변경하거나 행·열 번호를 함께 출력할 때 유용하다.
for row in range(len(distance)):
for column in range(len(distance[row])):
print(row, column, distance[row][column])
값만 필요할 때는 직접 순회가 간결하고, 위치 정보가 필요할 때는 인덱스 순회가 적합하다. 모든 행의 길이가 같지 않더라도 len(distance[row])를 사용하면 현재 행의 실제 열 개수만큼 안전하게 반복한다.
3. 순회 흐름의 이해
중첩 반복은 첫 번째 행의 모든 열을 처리한 다음 두 번째 행의 모든 열을 처리하는 방식으로 진행된다. 행이 n개이고 각 행에 m개의 원소가 있다면 안쪽 명령은 총 n×m번 실행된다. 표 전체 합계, 특정 조건을 만족하는 값의 개수, 행별 최댓값을 구하는 작업이 모두 이 순회 구조에서 출발한다.
핵심 개념 정리
2차원 리스트: 내부 원소로 리스트를 가지는 리스트이며 표처럼 행과 열로 해석할 수 있다.
접근: data[row][column]처럼 첫 인덱스로 행을, 두 번째 인덱스로 그 행의 열 원소를 선택한다.
크기: len(data)는 행 수, len(data[row])는 해당 행의 열 수이다.
정적 생성: 값이 정해져 있으면 내부 리스트들을 직접 나열한다.
동적 생성: 바깥 반복문에서 새 행을 만들고 안쪽 반복문으로 값을 채운 뒤 완성된 행을 바깥 리스트에 추가한다.
순회: 바깥 반복은 행, 안쪽 반복은 현재 행의 원소를 차례로 처리한다.
최종 정리
2차원 리스트는 별도의 새로운 자료형이 아니라 리스트를 원소로 갖는 리스트이다. 이 중첩 관계를 이해하면 두 번의 인덱싱과 두 단계의 반복이 자연스럽게 연결되며, 표 형태 데이터를 생성·검색·변경·출력할 수 있다.
예상문제 20선
1. 2차원 리스트에 대한 설명으로 가장 적절한 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ①
2차원 리스트는 바깥 리스트의 각 원소가 다시 내부 리스트인 중첩 구조이다.
2. data[2]가 반환하는 것은 일반적으로 무엇인가?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
인덱스 하나는 바깥 리스트의 원소인 내부 리스트 하나, 즉 한 행을 선택한다.
3. 2차원 리스트의 1번 행, 3번 열 원소에 접근하는 표현은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③
첫 대괄호로 내부 리스트를 고르고 두 번째 대괄호로 그 행의 원소를 고른다.
4. 파이썬 리스트의 첫 번째 원소 인덱스는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
파이썬의 순차 자료형은 0부터 인덱스를 부여한다.
5. 행의 개수를 구하는 표현은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
바깥 리스트의 원소 수가 내부 리스트, 즉 행의 개수이다.
6. 0번 행의 열 개수를 구하는 표현은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③data[0]은 첫 행 리스트이고 그 길이가 해당 행의 열 개수이다.
7. 거리표의 주대각선 값이 0인 이유는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
같은 도시에서 같은 도시까지 이동 거리는 0이므로 행과 열 인덱스가 같은 위치는 0이다.
8. 2차원 리스트의 원소 값을 바꾸는 올바른 표현은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ①
리스트는 변경 가능하므로 특정 행과 열 위치를 지정해 새 값을 할당할 수 있다.
9. 값이 미리 정해진 2차원 리스트를 만드는 적절한 방법은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
정적 생성은 각 행을 내부 리스트로 작성하고 이를 바깥 리스트의 원소로 나열한다.
10. 동적 생성 코드에서 row = []의 위치로 적절한 곳은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③
새 행을 시작할 때마다 빈 리스트를 만들어야 각 행이 독립된 리스트가 된다.
11. 동적 생성에서 완성된 row를 바깥 리스트에 추가할 시점은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
한 행의 모든 열 원소가 준비된 뒤 그 행을 바깥 리스트에 추가해야 완전한 구조가 된다.
12. 강의록의 난수 생성 범위 randint(0, 99)에 포함되는 값은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ①randint(a, b)는 양 끝값 a와 b를 모두 포함한 정수 난수를 만든다.
13. 2차원 리스트 전체 원소를 순회할 때 필요한 기본 구조는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
바깥 반복문이 행을, 안쪽 반복문이 해당 행의 열 원소를 순회한다.
14. for dep in distance:에서 dep가 받는 값은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③
바깥 리스트를 직접 순회하면 반복 변수는 각 원소인 내부 리스트를 차례로 받는다.
15. 중첩 반복에서 안쪽 반복문의 역할은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
바깥 반복이 선택한 현재 행 안에서 안쪽 반복이 각 값을 순서대로 처리한다.
16. 행 위치와 열 위치가 모두 필요할 때 적합한 순회 방식은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ①
인덱스 범위를 반복하면 현재 행·열 번호와 그 위치의 값을 함께 사용할 수 있다.
17. 행 3개, 각 행의 열 4개를 모두 순회할 때 안쪽 명령의 총 실행 횟수는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
각 3개 행에서 4개 열을 처리하므로 3×4인 12회 실행된다.
18. 길이가 서로 다른 내부 리스트도 2차원 리스트에 저장할 수 있는가?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③
파이썬은 행 길이를 강제하지 않지만 행렬이나 표처럼 처리하려면 동일한 열 수가 일반적이다.
19. 같은 내부 리스트를 여러 행이 공유할 때 생길 수 있는 문제는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
여러 위치가 같은 내부 리스트 객체를 참조하면 하나를 변경할 때 모두 같은 변경을 관찰한다.
20. print(des, end=" ") 뒤에 행마다 print()를 호출하는 이유는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ①
안쪽 반복에서 값을 같은 줄에 출력하고 행 처리가 끝나면 빈 print()로 다음 줄로 이동한다.
댓글
댓글 쓰기