기본 콘텐츠로 건너뛰기

방송대 파이썬프로그래밍기초 8강: 컬렉션 선택과 리스트 조작

0-썸네일-요약노트-파이썬프로그래밍기초-8강

방송대 파이썬프로그래밍기초 8강: 컬렉션 선택과 리스트 조작

여러 값을 한 변수에 담았다고 해서 모두 같은 방식으로 다룰 수 있는 것은 아니다. 순서가 필요한지, 중복을 허용할지, 값을 바꿀지, 이름으로 찾을지에 따라 적합한 컬렉션이 달라진다. 이 기준으로 리스트·튜플·세트·딕셔너리를 구분한 뒤, 리스트의 인덱스·메소드·슬라이싱·컴프리헨션을 하나의 데이터 처리 흐름으로 연결해 보자.

컬렉션은 여러 데이터를 하나의 구조로 묶는다

컬렉션(collection)은 여러 데이터를 하나의 구조로 저장하고 다루기 위한 자료구조의 총칭이다. 각각의 값을 별도 변수에 흩어 놓는 대신 하나의 묶음으로 만들면 원소 전체를 반복 처리하거나 필요한 값을 찾고, 일부를 골라 새 결과로 바꾸기 쉬워진다. 데이터 분석·네트워크 통신·파일 처리처럼 많은 값을 다루는 프로그램에서 컬렉션은 알고리즘의 입력과 결과를 조직하는 기본 도구가 된다.

이 강의에서 컬렉션은 저장·접근 방식에 따라 시퀀스, 비시퀀스, 매핑으로 구분한다. 리스트와 튜플은 원소의 순서를 유지하는 시퀀스다. 세트는 중복을 허용하지 않고 순서를 전제로 하지 않는 비시퀀스이며, 딕셔너리는 키와 값을 연결하는 매핑이다. 따라서 “여러 값을 담는다”는 공통점만 보고 자료구조를 고르면 부족하다.

선택을 위한 네 질문: 위치 순서가 의미 있는가? 중복을 남겨야 하는가? 생성 후 원소를 바꿔야 하는가? 위치가 아니라 이름인 키로 값을 찾아야 하는가? 이 네 질문에 답하면 자료구조 선택의 근거가 드러난다.

자료구조핵심 관계변경대표 생성적합한 요구
리스트순서 있는 원소가능[], list()순서대로 저장하면서 추가·삭제·수정
튜플순서 있는 원소불가능(), tuple()한 번 정한 묶음을 안정적으로 유지
세트서로 다른 원소가능{1, 2}, set()중복 제거와 고유값 관리
딕셔너리키와 값의 대응가능{"키": 181}, dict()키로 대응 값을 빠르게 검색

같은 데이터도 필요한 관계에 따라 구조가 달라진다

학습을 위해 가상의 행사 입장 기록을 생각해 보자. 입장 순서와 재입장 횟수까지 남기려면 ["A12", "B07", "A12"] 같은 리스트가 적합하다. 출입구 좌표처럼 생성 뒤 바뀌지 않아야 할 두 값은 (37, 12) 같은 튜플로 묶을 수 있다. 실제 방문자 번호만 필요하면 set()으로 중복을 제거하고, 참가자 번호로 이름을 찾으려면 {"A12": "민지"} 같은 딕셔너리를 사용한다.

요구 문장판단 단서선택선택하지 않은 구조의 문제
입장한 차례와 횟수를 그대로 남긴다순서·중복 필요리스트세트는 중복과 위치 정보를 잃는다
확정한 좌표 쌍을 바꾸지 않는다순서·불변튜플리스트는 의도하지 않은 변경을 허용한다
방문한 사람의 고유 번호만 남긴다고유성세트리스트는 중복을 별도로 제거해야 한다
번호로 이름을 찾는다키→값딕셔너리위치 기반 시퀀스는 번호와 이름의 대응을 직접 표현하지 못한다

오개념 교정: 빈 중괄호 {}는 빈 세트가 아니라 빈 딕셔너리를 만든다. 중괄호가 세트 표기에도 쓰여 생기는 혼동이다. 원소가 있는 세트는 {1, 2}처럼 만들 수 있지만, 빈 세트는 반드시 set()으로 생성한다.

리스트는 순서가 있고 변경 가능한 시퀀스다

리스트(list)는 원소를 차례대로 저장하며, 생성 후 원소를 추가·삭제·수정할 수 있는 가변(mutable) 시퀀스다. 하나의 리스트에 서로 다른 데이터 타입을 함께 저장할 수도 있다. 다음 강의 예시는 키, 몸무게, 눈 색, 머리 색을 순서대로 담고 마지막에 값을 하나 더 추가한다.

body = [181, 78, "dark brown", "black"]
body.append(78)
print(body)
# [181, 78, 'dark brown', 'black', 78]

append(78)은 리스트 끝에 78을 하나의 새 원소로 붙인다. 기존의 78을 바꾸거나 중복을 없애지 않는다. 리스트는 중복을 허용하므로 두 개의 78이 서로 다른 위치에 남는다. 반면 같은 값들을 튜플 (181, 78, "dark brown", "black")로 만들면 순서는 유지되지만 생성 후 원소를 직접 바꿀 수 없다.

잘못된 접근: “튜플이 리스트보다 언제나 좋다”거나 그 반대로 외우면 목적을 놓친다. 기록이 계속 갱신되면 리스트의 가변성이 장점이고, 실수로 바뀌어서는 안 되는 확정 묶음이면 튜플의 불변성이 장점이다.

인덱스는 한 원소를, 슬라이스는 부분 리스트를 고른다

리스트의 한 원소는 대괄호와 인덱스로 접근한다. 양수 인덱스는 왼쪽에서 0부터 시작하고, 음수 인덱스는 오른쪽에서 -1부터 시작한다. hei_list = [1, 5, 14, 26, 31]에서 hei_list[4]는 31이고 hei_list[-3]은 14다.

원소15142631
양수 인덱스01234
음수 인덱스-5-4-3-2-1

슬라이싱은 리스트[시작:끝:간격] 형식으로 부분 리스트를 만든다. 시작 위치는 포함하지만 끝 위치는 포함하지 않는다. 값을 생략하면 진행 방향에 맞는 처음이나 끝까지 선택한다.

hei_list = [1, 5, 14, 26, 31]

print(hei_list[:3])    # [1, 5, 14]
print(hei_list[-3:])   # [14, 26, 31]
print(hei_list[::2])   # [1, 14, 31]
print(hei_list[::-1])  # [31, 26, 14, 5, 1]
표현시작끝간격선택 결과
[:3]처음3 직전1인덱스 0·1·2
[-3:]뒤에서 셋째끝1마지막 세 원소
[::2]처음끝2한 칸씩 건너 선택
[::-1]오른쪽 끝왼쪽 끝 너머-1역순의 새 리스트

경계 검산법: 슬라이스를 읽을 때 “시작은 포함, 끝은 제외, 간격만큼 이동”을 말로 풀고 실제로 방문하는 인덱스를 적는다. 이 방법은 끝 인덱스를 포함한다고 착각하는 오류를 줄인다.

연산자와 내장 함수는 리스트 전체의 성질을 묻는다

in과 not in은 원소의 포함 여부를 확인한다. +는 두 시퀀스를 이어 새 시퀀스를 만들고, *는 시퀀스를 지정 횟수만큼 반복해 이어 붙인다. 비교 연산자는 앞 원소부터 차례로 비교한다. 이때 숫자 곱셈과 달리 [1, 2] * 3은 각 원소를 세 배로 만드는 것이 아니라 [1, 2, 1, 2, 1, 2]를 만든다.

도구질문 또는 동작예시 결과
26 in hei_list26이 원소로 있는가?True
[1, 5] + [7]두 리스트를 연결[1, 5, 7]
[1, 7] * 2리스트를 두 번 반복 연결[1, 7, 1, 7]
len(hei_list)원소 개수5
min(hei_list)가장 작은 원소1
max(hei_list)가장 큰 원소31
sum(hei_list)모든 수치 원소의 합77

len, min, max, sum은 리스트만의 메소드가 아니라 여러 객체에 적용되는 내장 함수다. 그래서 len(hei_list)처럼 함수 이름 뒤 괄호 안에 리스트를 전달한다. 다음 절의 메소드는 리스트 객체에 결합되어 hei_list.append(27)처럼 점 표기법을 쓴다는 차이가 있다.

리스트 메소드는 원소를 찾거나 제자리에서 바꾼다

메소드는 클래스 내부에 정의되어 특정 객체의 데이터에 작업을 수행하는 함수다. 리스트에서는 리스트.메소드(입력값) 형식으로 사용한다. 어떤 메소드는 정보를 반환하고, 어떤 메소드는 리스트 자체를 변경하므로 호출 뒤 상태를 구분해야 한다.

메소드핵심 동작주의점
append(x)끝에 x를 한 원소로 추가리스트 자체가 바뀐다
count(x)x의 출현 횟수 반환리스트는 바뀌지 않는다
index(x)처음 나온 x의 인덱스 반환중복이면 첫 위치만 반환
pop(i)i 위치 원소를 제거하고 반환위치 기준이며 리스트가 바뀐다
insert(i, x)i 위치에 x를 삽입뒤 원소들의 위치가 밀린다
remove(x)처음 만난 x를 제거값 기준이며 같은 값 전부를 지우지 않는다
reverse()원소 순서를 제자리에서 반전역순 상태로 원본이 바뀐다
sort()원소를 제자리에서 오름차순 정렬정렬된 원본을 이후 코드가 사용한다
values = [5, 8, 5, 9]
first_five = values.index(5)  # 0
count_five = values.count(5)  # 2
values.remove(5)
print(values)                 # [8, 5, 9]

오개념 교정: remove(5)는 값 5를 모두 제거하지 않는다. 왼쪽에서 처음 만난 하나만 지우므로 두 번째 5는 남는다. 모든 5를 제외한 새 리스트가 필요하다면 뒤에서 다룰 조건부 리스트 컴프리헨션이 더 분명하다.

유효 평균과 중앙값은 복사·제거·정렬의 순서로 구한다

강의 문제는 점수 리스트에서 가장 높은 점수와 가장 낮은 점수를 하나씩 제외한 뒤, 나머지의 평균과 중앙값을 요구한다. 여기서 “하나씩”은 같은 최댓값이나 최솟값이 여러 개여도 각각 한 번만 제거한다는 뜻이다. remove(max(scores))와 remove(min(scores))가 이 요구에 맞는다.

scores = [9, 8, 10, 7, 9, 8, 10, 6, 9, 8,
          7, 9, 10, 8, 9, 5, 8, 9, 7]

valid = scores[:]              # 원본을 보존하는 얕은 복사
valid.remove(max(valid))       # 최댓값 10 하나 제거
valid.remove(min(valid))       # 최솟값 5 하나 제거

average = sum(valid) / len(valid)
valid.sort()
middle = valid[len(valid) // 2]

print(f"유효 평균값은 {average:.2f}입니다")
print(f"중앙값은 {middle}입니다")
단계개수합 또는 상태검산
원본19최댓값 10, 최솟값 5제외 전 경계값 확인
양끝값 하나씩 제거17합 14119 - 2 = 17
평균 계산17141 / 17 = 8.2941…소수 둘째 자리 8.29
오름차순 정렬17가운데 인덱스 17 // 2 = 8인덱스 8의 값 8

원본 점수도 이후에 사용해야 할 수 있으므로 scores[:]로 복사본을 만든 뒤 변경했다. 중앙값은 반드시 정렬된 자료에서 가운데 위치를 골라야 한다. 원소가 17개이므로 0부터 센 가운데 인덱스는 8이고, 그 값은 8이다. 따라서 유효 평균은 8.29, 중앙값은 8이다.

실패 원인: 원본에서 바로 제거하면 이후 비교에 필요한 원자료가 달라진다. 또한 정렬 전에 단순히 인덱스 8을 읽으면 ‘입력 순서의 가운데’일 뿐 중앙값이 아니다. 통계의 조건과 리스트 상태 변화를 함께 확인해야 한다.

리스트 컴프리헨션은 반복·변환·선택을 한 표현으로 묶는다

리스트 컴프리헨션(list comprehension)은 반복 가능한 자료에서 원소를 하나씩 받아 표현식을 계산하고 새 리스트를 만든다. 기본 형식은 [표현식 for 변수 in 리스트]이며, 조건을 붙이면 [표현식 for 변수 in 리스트 if 조건]이 된다. 앞부분은 새 리스트에 넣을 값, 뒤의 for는 반복 대상, 마지막 if는 남길 원소의 조건이다.

hei_list = [1, 5, 14, 26, 31]

hei_square = [h ** 2 for h in hei_list]
hei_len = [len(str(h)) for h in hei_list]
hei_even = [h for h in hei_list if h % 2 == 0]
hei_hi = [h for h in hei_list if h > 20]

print(hei_square)  # [1, 25, 196, 676, 961]
print(hei_len)     # [1, 1, 2, 2, 2]
print(hei_even)    # [14, 26]
print(hei_hi)      # [26, 31]
질문표현식원소 h가 하는 일
모든 값을 제곱하려면?[h ** 2 for h in hei_list]각 h를 변환해 결과에 넣는다
각 수의 자릿수는?[len(str(h)) for h in hei_list]문자열 길이로 변환한다
짝수만 남기려면?[h for h in hei_list if h % 2 == 0]조건이 참인 h만 넣는다
20보다 큰 값만?[h for h in hei_list if h > 20]경계를 통과한 h만 넣는다

컴프리헨션은 일반 for문과 append()를 짧게 표현할 수 있고 변환 의도를 한눈에 보여 준다. 그러나 여러 단계의 복잡한 조건이나 부수 효과가 섞이면 한 줄이 오히려 읽기 어려워질 수 있다. 간결함은 목적이 선명할 때의 장점이지, 모든 반복을 무조건 한 줄로 줄이라는 규칙은 아니다.

이동평균은 슬라이싱과 컴프리헨션을 연결한 창 계산이다

연속 3일 종가의 평균은 시작 인덱스가 하나씩 이동하는 길이 3의 창(window)을 생각하면 된다. 시작일이 i일 때 필요한 구간은 prices[i:i + 3]이고, 마지막 시작 인덱스는 전체 길이에서 창 길이를 뺀 값이다. 각 창의 합을 3으로 나누면 3일 이동평균이 된다.

# 직접 구성한 학습용 예제
prices = [10000, 10800, 11600, 11200, 12100]
window = 3

moving = [sum(prices[i:i + window]) / window
          for i in range(len(prices) - window + 1)]

result = [(i, avg) for i, avg in enumerate(moving)
          if avg > 11000]

print(moving)  # [10800.0, 11200.0, 11633.333...]
print(result)  # [(1, 11200.0), (2, 11633.333...)]
시작 i슬라이스합3일 평균11000 초과
0[10000, 10800, 11600]3240010800.00아니요
1[10800, 11600, 11200]3360011200.00예
2[11600, 11200, 12100]3490011633.33예

len(prices) - window + 1은 가능한 시작 위치의 개수다. 원소가 5개이고 창 길이가 3이면 시작 위치는 0·1·2의 세 곳이다. + 1을 빠뜨리면 마지막 유효 구간을 계산하지 못한다. 강의의 30영업일 문제도 같은 구조로 3일 평균을 만들고 11,000원 초과인 시작 인덱스와 값을 선별한다.

재사용 가능한 문제 해결 순서: 원자료의 보존 여부를 정한다 → 한 원소인지 구간인지 선택한다 → 변경 작업과 새 리스트 생성을 구분한다 → 필요한 구간의 시작·끝·간격을 적는다 → 변환식과 필터 조건을 분리한다 → 개수·합·경계 인덱스로 결과를 검산한다.

핵심 정리

  • 컬렉션은 여러 데이터를 한 구조로 묶으며 순서·중복·변경·키 검색 요구에 따라 종류를 고른다.
  • 리스트는 순서가 있고 변경 가능하며 중복과 서로 다른 타입의 원소를 허용한다.
  • 튜플은 순서가 있지만 변경할 수 없고, 세트는 고유값을, 딕셔너리는 키와 값의 대응을 표현한다.
  • 빈 세트는 {}가 아니라 set()으로 생성한다.
  • 인덱싱은 한 원소를 선택하고 슬라이싱은 시작:끝:간격에 따라 새 부분 리스트를 만든다.
  • len·min·max·sum은 전체 성질을 계산하고, 리스트 메소드는 검색이나 제자리 변경을 수행한다.
  • remove(x)는 처음 만난 x 하나만 지우며, sort()와 reverse()는 원본 순서를 바꾼다.
  • 리스트 컴프리헨션은 반복에 변환식과 선택 조건을 결합해 새 리스트를 만든다.
  • 통계값과 이동평균 문제는 원본 보존, 구간 경계, 원소 개수와 합을 차례로 검산해야 한다.

여러 값을 만났을 때 곧바로 리스트부터 쓰지 말고 데이터 사이에서 보존할 관계를 먼저 말로 적는다. 순서·중복·변경·키 검색을 기준으로 컬렉션을 고르고, 리스트를 골랐다면 한 원소는 인덱스, 연속 구간은 슬라이스, 원본 변경은 메소드, 새 변환 결과는 컴프리헨션으로 구분한다. 마지막에는 끝 인덱스가 제외되는지, 메소드가 원본을 바꾸는지, 결과 개수와 합이 요구 조건과 맞는지를 확인하면 컬렉션 문제를 일관된 절차로 해결할 수 있다.

예상문제 10선

1. 컬렉션의 설명으로 가장 적절한 것은?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ③

  • ① 오답: 컬렉션은 한 숫자의 계산 기호가 아니라 여러 값을 조직하는 구조다.
  • ② 오답: 특히 리스트는 서로 다른 타입의 값도 함께 담을 수 있다.
  • ③ 정답: 여러 데이터를 묶고 효율적으로 접근·관리하게 하는 컬렉션의 목적을 설명한다.
  • ④ 오답: 컬렉션은 반복 처리의 대상이 될 수 있으며 반복을 금지하지 않는다.

2. 입장 순서는 필요 없고 방문자 번호의 중복만 제거하려 한다. 가장 적합한 자료구조는?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ①

  • ① 정답: 세트는 중복을 허용하지 않아 고유 방문자만 남기는 요구에 맞는다.
  • ② 오답: 튜플은 순서 있는 불변 묶음이며 중복을 자동 제거하지 않는다.
  • ③ 오답: 리스트는 순서와 중복을 모두 유지한다.
  • ④ 오답: 딕셔너리는 키와 값의 대응이 필요할 때 적합하다.

3. 빈 세트를 올바르게 생성하는 코드는?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ④

  • ① 오답: 빈 중괄호는 빈 딕셔너리를 만든다.
  • ② 오답: 대괄호는 빈 리스트를 만든다.
  • ③ 오답: 빈 소괄호는 빈 튜플을 만든다.
  • ④ 정답: 원소가 없는 세트는 set() 생성자로 만든다.

4. values = [3, 6, 9, 12, 15]일 때 values[-3]의 값은?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ②

  • ① 오답: 6은 오른쪽에서 네 번째인 인덱스 -4다.
  • ② 정답: 오른쪽부터 -1은 15, -2는 12, -3은 9다.
  • ③ 오답: 12는 인덱스 -2다.
  • ④ 오답: 15는 마지막 원소이므로 인덱스 -1이다.

5. numbers = [1, 5, 14, 26, 31]일 때 numbers[1:5:2]의 결과는?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ①

  • ① 정답: 인덱스 1에서 시작해 2씩 이동하므로 인덱스 1과 3의 값이 선택된다.
  • ② 오답: 시작을 0으로 읽었을 때 나오는 [::2]의 결과다.
  • ③ 오답: 간격 2를 적용하지 않고 1부터 끝까지 선택했다.
  • ④ 오답: 간격을 적용하면서도 마지막 31을 잘못 포함했다.

6. pop(i)와 remove(x)를 올바르게 구분한 것은?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ③

  • ① 오답: remove도 처음 일치한 값 하나만 제거한다.
  • ② 오답: 두 메소드의 기준을 반대로 설명했다.
  • ③ 정답: 위치 기준 제거·반환과 값 기준 첫 일치 제거의 차이를 정확히 나타낸다.
  • ④ 오답: 두 메소드 모두 대상 리스트 자체를 변경한다.

7. 정렬된 새 리스트를 변수 ordered에 저장하려고 ordered = values.sort()라고 작성했다. 문제를 바로잡은 설명은?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ②

  • ① 오답: 한 번의 호출로 리스트 전체를 정렬한다.
  • ② 정답: 원본 보존이 필요하면 ordered = values[:] 후 ordered.sort()처럼 상태 변경 대상을 분리한다.
  • ③ 오답: 리스트는 정렬 가능하며 세트는 순서 보존 목적에 맞지 않는다.
  • ④ 오답: sum()은 수치 합을 반환하며 정렬된 리스트를 만들지 않는다.

8. hei_list에서 20보다 큰 원소만 새 리스트로 만드는 빈칸에 들어갈 표현은? [h for h in hei_list ____]

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ④

  • ① 오답: 반복 대상은 이미 for h in hei_list로 지정되었다.
  • ② 오답: 컴프리헨션의 뒤쪽 필터에는 while이 아니라 if를 쓴다.
  • ③ 오답: 컴프리헨션은 결과 리스트를 직접 만들므로 append 문법을 넣지 않는다.
  • ④ 정답: 뒤의 if가 각 h를 검사해 20보다 큰 값만 남긴다.

9. 원소가 30개인 종가 리스트에서 길이 3의 이동평균 창을 한 칸씩 옮기면 계산되는 평균은 모두 몇 개인가?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ③

  • ① 오답: 30 - 3만 계산해 마지막 유효 시작 위치를 빠뜨렸다.
  • ② 오답: 마지막 두 시작 위치에는 뒤 원소가 부족해 길이 3 창을 만들 수 없다.
  • ③ 정답: 가능한 창의 수는 30 - 3 + 1 = 28이다.
  • ④ 오답: 30을 3으로 나눈 값이며 창이 서로 겹쳐 이동한다는 점을 반영하지 않았다.

10. 점수의 최댓값과 최솟값을 하나씩 제외해 평균과 중앙값을 구하면서 원본도 보존하려 한다. 가장 적절한 절차는?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ②

  • ① 오답: 세트는 모든 중복과 순서를 잃어 ‘하나씩 제외’라는 요구를 바꾼다.
  • ② 정답: 원본 보존, 한 번씩 제거, 평균의 새 개수, 중앙값의 정렬 조건을 모두 충족한다.
  • ③ 오답: 중앙값은 정렬 뒤 골라야 하며 평균의 분모도 제거 후 개수여야 한다.
  • ④ 오답: 유효 평균은 극단값을 뺀 나머지 점수 전체로 계산한다.

참고 자료와 작성 기준

이 글은 해당 차시 강의자료를 바탕으로 학습 목적에 맞게 재구성한 비공식 학습자료입니다. 컬렉션 선택 질문표, 행사 입장 기록 사례, 인덱스·슬라이스 경계표, 유효 평균과 중앙값의 상태 추적, 이동평균 창 예제와 문제 해결 순서는 학습자가 개념을 직접 판별하고 검산하도록 새로 구성했습니다.

  • 작성·편집: 올에이클래스 학습연구팀
  • 주요 근거: 한국방송통신대학교 컴퓨터과학과, 「파이썬프로그래밍기초」 8강 컬렉션 1 강의자료, 2026
  • 보충 자료: 외부 자료를 사용하지 않음
  • 편집 원칙: 올에이클래스 편집 정책
  • 최종 내용 검토: 2026-09-07

댓글