6주차 · 자료구조
이번 주 목표는 자료구조 4개를 모두 외우는 것이 아니라, 먼저 아래 4가지를 분명히 구분하는 것입니다.
list는 순서 있는 여러 값을 번호(index)로 읽는다.tuple은 list와 비슷하지만 만든 뒤 값을 바꾸지 않는다.dict는 번호가 아니라 key 이름으로 값을 찾는다.set은 중복 제거와 포함 여부 확인에 쓴다.append,pop,get,add처럼 지금 꼭 필요한 기본 동작을 사용할 수 있다.for반복문으로list/tuple/dict를 순회하며 값을 읽고 간단한 누적·출력을 할 수 있다.- 문제를 보고 “이건 list / tuple / dict / set 중 무엇을 써야 하는가?”를 설명할 수 있다.
1) 왜 자료구조가 필요한가?
Section titled “1) 왜 자료구조가 필요한가?”자료구조는 데이터를 정리해서 담는 방식입니다.
flowchart TB A[같은 데이터] --> B[어떻게 담는가] B --> C[읽기 쉬운가] B --> D[수정이 쉬운가] B --> E[버그가 줄어드는가]
예를 들어 학생 이름 100명을 다룬다고 해 봅시다.
- 순서대로 출력하려면
list - 학번으로 바로 찾으려면
dict - 중복 이름을 제거하려면
set
즉, 데이터는 같아도 담는 방식이 다르면 코드가 달라집니다.
2) 값을 꺼내는 방식부터 구분하기
Section titled “2) 값을 꺼내는 방식부터 구분하기”처음 배우는 학생이 가장 먼저 구분해야 하는 것은 “이 구조에서 값을 어떻게 읽는가” 입니다.
| 구조 | 값을 읽는 방식 | 예시 | 이 구조가 답하는 질문 |
|---|---|---|---|
list | index(번호) | scores[0] | “첫 번째 값은?” |
tuple | index(번호) | point[1] | “두 번째 값은?” |
dict | key(이름표) | sensor["temp"] | “temp 값은?” |
set | 포함 여부 | "ok" in tags | “이 값이 들어 있나?” |
3) list — 가장 먼저 익힐 구조
Section titled “3) list — 가장 먼저 익힐 구조”list는 순서가 있는 여러 값을 담는 가장 기본적인 자료구조입니다.
list 만들기와 읽기
Section titled “list 만들기와 읽기”values = [12.4, 11.8, 12.9, 13.1, 12.2]print(values[0]) # 첫 번째 값print(values[-1]) # 마지막 값| 인덱스 | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 값 | 12.4 | 11.8 | 12.9 | 13.1 | 12.2 |
| 음수 인덱스 | -5 | -4 | -3 | -2 | -1 |
values[0]→ 첫 번째 값values[-1]→ 마지막 값
슬라이싱으로 여러 값 한 번에 보기
Section titled “슬라이싱으로 여러 값 한 번에 보기”values = [12.4, 11.8, 12.9, 13.1, 12.2]print(values[1:4])출력:
[11.8, 12.9, 13.1]슬라이싱 규칙은 다음 한 줄로 기억하면 됩니다.
start <= index < end
즉, values[1:4]는 1번부터 시작해서 4번 직전까지 가져옵니다.
list는 수정할 수 있다
Section titled “list는 수정할 수 있다”values = [12.4, 11.8, 12.9]values[1] = 12.0print(values) # [12.4, 12.0, 12.9]list에서 지금 꼭 익힐 동작
Section titled “list에서 지금 꼭 익힐 동작”| 동작 | 코드 | 의미 |
|---|---|---|
| 읽기 | values[0] | 번호로 값 읽기 |
| 구간 읽기 | values[1:3] | 여러 값 한 번에 가져오기 |
| 수정 | values[1] = 12.0 | 기존 값 바꾸기 |
| 맨 끝 추가 | values.append(14.0) | 값 1개 추가 |
| 맨 끝 꺼내기 | values.pop() | 마지막 값을 꺼내며 삭제 |
| 길이 확인 | len(values) | 값이 몇 개인지 확인 |
values = [12.4, 11.8, 12.9]values.append(14.0)last = values.pop()print(values) # [12.4, 11.8, 12.9]print(last) # 14.04) tuple — list와 비슷하지만 바꾸지 않는 묶음
Section titled “4) tuple — list와 비슷하지만 바꾸지 않는 묶음”tuple은 순서가 있는 값 묶음이지만, 만든 뒤에는 값을 바꾸지 않습니다.
tuple은 언제 쓰나?
Section titled “tuple은 언제 쓰나?”- 좌표
(x, y) - 날짜
(2026, 4, 9) - RGB 색
(255, 0, 0) - “읽기만 하고 수정하지 않을 값 묶음”
point = (3, 5)print(point[0])print(point[1])# point[0] = 7 # TypeErrorlist와 tuple의 차이
Section titled “list와 tuple의 차이”| 구조 | 읽기 | 수정 | 어울리는 상황 |
|---|---|---|---|
list | 가능 | 가능 | 계속 바뀌는 데이터 |
tuple | 가능 | 불가 | 고정된 값 묶음 |
tuple에서 기억할 점
Section titled “tuple에서 기억할 점”- 읽는 방법은 list와 같다. →
point[0] - 하지만 값 변경은 안 된다.
- 처음 배우는 단계에서는 “읽기 전용 list 비슷한 것” 으로 이해해도 충분하다.
5) dict — 이름표(key)로 찾는 구조
Section titled “5) dict — 이름표(key)로 찾는 구조”dict는 값을 번호로 찾지 않고, key 이름으로 찾는 자료구조입니다.
sensor = {"id": "A01", "temp": 23.4, "status": "ok"}print(sensor["id"])print(sensor["temp"])dict 기본 읽기 / 추가 / 수정
Section titled “dict 기본 읽기 / 추가 / 수정”sensor = {"id": "A01", "temp": 23.4}
print(sensor["id"]) # 읽기sensor["status"] = "ok" # 새 key 추가sensor["temp"] = 24.0 # 기존 key 값 수정print(sensor)없는 key는 get()으로 안전하게 읽기
Section titled “없는 key는 get()으로 안전하게 읽기”sensor = {"id": "A01", "temp": 23.4}print(sensor.get("owner", "미등록"))key가 있는지 먼저 확인하기
Section titled “key가 있는지 먼저 확인하기”sensor = {"id": "A01", "temp": 23.4}
if "temp" in sensor: print(sensor["temp"])dict에서 지금 꼭 익힐 동작
Section titled “dict에서 지금 꼭 익힐 동작”| 동작 | 코드 | 의미 |
|---|---|---|
| key로 읽기 | sensor["temp"] | key 이름으로 값 읽기 |
| 추가/수정 | sensor["status"] = "ok" | key:value 넣기 |
| 안전 조회 | sensor.get("owner", "미등록") | 없으면 기본값 반환 |
| 존재 확인 | "temp" in sensor | 이 key가 있는지 확인 |
| 길이 확인 | len(sensor) | key가 몇 개인지 확인 |
6) set — 중복 제거와 포함 여부 확인
Section titled “6) set — 중복 제거와 포함 여부 확인”set은 중복 없는 값 모음입니다.
tags = {"ok", "warn"}tags.add("offline")print("ok" in tags)중복 제거에 가장 자주 쓴다
Section titled “중복 제거에 가장 자주 쓴다”raw_tags = ["ok", "warn", "ok", "ok", "offline"]unique_tags = set(raw_tags)print(unique_tags)print(sorted(unique_tags))set(raw_tags)→ 중복 제거"ok" in unique_tags→ 포함 여부 확인sorted(unique_tags)→ 보기 좋게 정렬해서 출력
set에서 지금 꼭 익힐 동작
Section titled “set에서 지금 꼭 익힐 동작”| 동작 | 코드 | 의미 |
|---|---|---|
| 생성 | set(["ok", "ok", "warn"]) | 중복 제거 |
| 추가 | tags.add("offline") | 값 1개 추가 |
| 포함 검사 | "ok" in tags | 값이 있는지 확인 |
| 정렬 출력 | sorted(tags) | 순서 있게 출력 |
| 길이 확인 | len(tags) | 고유 값 개수 확인 |
7) 자료구조를 반복문과 함께 읽기
Section titled “7) 자료구조를 반복문과 함께 읽기”4주차의 for문은 range()와 함께 썼습니다.
이번 주부터는 숫자 범위 대신 자료구조 자체를 반복할 수 있어야 합니다.
| 반복 대상 | 기본 형태 | 반복마다 꺼내는 값 |
|---|---|---|
list | for value in values: | 각 원소 |
tuple | for value in point: | 각 원소 |
dict | for key in sensor: | 각 key |
dict.items() | for key, value in sensor.items(): | (key, value) 쌍 |
A. list 반복: 값을 하나씩 읽어 평균 준비하기
Section titled “A. list 반복: 값을 하나씩 읽어 평균 준비하기”temperatures = [23.1, 22.8, 24.0, 23.7]total = 0
for temp in temperatures: print("current:", temp) total += temp
average = total / len(temperatures)print("average:", average)temp에는 list의 원소가 하나씩 순서대로 들어갑니다.range(len(temperatures))를 쓰지 않아도, 값 자체를 바로 꺼낼 수 있습니다.- 이런 방식이 읽기 전용 순회에서는 더 자연스럽고 실수가 적습니다.
B. tuple 반복: 고정된 값 묶음을 차례대로 읽기
Section titled “B. tuple 반복: 고정된 값 묶음을 차례대로 읽기”point = (3, 5)
for value in point: print("coord:", value)- tuple도 list처럼
for문으로 순회할 수 있습니다. - 단, tuple은 읽기만 하고 수정하지 않습니다.
C. dict 반복: key만 돌거나, key와 value를 함께 돌기
Section titled “C. dict 반복: key만 돌거나, key와 value를 함께 돌기”sensor = {"A01": "OK", "A02": "WARN", "A03": "OK"}
for sensor_id in sensor: print(sensor_id, sensor[sensor_id])for sensor_id in sensor:는 key를 하나씩 꺼냅니다.- value가 필요하면
sensor[sensor_id]로 다시 읽습니다.
sensor = {"A01": "OK", "A02": "WARN", "A03": "OK"}
for sensor_id, status in sensor.items(): print(sensor_id, status)items()는(key, value)쌍을 꺼냅니다.- 그래서 출력·집계·검사에서는
items()가 더 편한 경우가 많습니다.
8) 한눈에 비교하기
Section titled “8) 한눈에 비교하기”이제 4개 구조를 다시 비교해 보면 더 이해가 쉽습니다.
| 구조 | 순서 | 수정 가능 | 값을 읽는 방식 | 가장 먼저 떠올릴 사용처 |
|---|---|---|---|---|
list | 있음 | 가능 | index | 여러 값을 순서대로 저장 |
tuple | 있음 | 불가 | index | 고정된 값 묶음 |
dict | 있음* | 가능 | key | 이름으로 값 찾기 |
set | index 순서 없음 | 가능 | 포함 여부 | 중복 제거 |
* Python 3.7+에서는 삽입 순서를 유지합니다.
9) 한 번 더 보면 좋은 method 참고표
Section titled “9) 한 번 더 보면 좋은 method 참고표”아래는 지금 당장 외우기보다, 필요할 때 찾아보는 2차 참고용입니다.
list 참고 method
Section titled “list 참고 method”| method | 의미 | 예시 |
|---|---|---|
insert(i, x) | 원하는 위치에 삽입 | nums.insert(1, 15) |
extend(seq) | 여러 값 이어 붙이기 | nums.extend([50, 60]) |
remove(x) | 값이 같은 첫 원소 삭제 | nums.remove(20) |
sort() | 리스트 자체 정렬 | nums.sort() |
count(x) | 값 개수 세기 | nums.count(10) |
index(x) | 값의 위치 찾기 | nums.index(30) |
dict 참고 method
Section titled “dict 참고 method”| method | 의미 | 예시 |
|---|---|---|
keys() | key들 보기 | sensor.keys() |
values() | value들 보기 | sensor.values() |
items() | (key, value) 쌍 보기 | sensor.items() |
update(other) | 여러 key를 한꺼번에 반영 | sensor.update({"status": "ok"}) |
pop(key) | key를 꺼내며 삭제 | sensor.pop("temp") |
set 참고 method
Section titled “set 참고 method”| method | 의미 | 예시 |
|---|---|---|
update(seq) | 여러 값 추가 | tags.update(["ok", "hold"]) |
remove(x) | 값 삭제 (없으면 에러) | tags.remove("warn") |
discard(x) | 값 삭제 (없어도 조용히 통과) | tags.discard("warn") |
10) 잘못된 예와 올바른 예
Section titled “10) 잘못된 예와 올바른 예”A. dict 없는 key를 바로 읽기
Section titled “A. dict 없는 key를 바로 읽기”Bad
lookup = {"A01": 23.4}print(lookup["A03"]) # KeyErrorGood
print(lookup.get("A03", "missing"))
if "A03" in lookup: print(lookup["A03"])else: print("A03 없음")B. list 복사 착각하기
Section titled “B. list 복사 착각하기”Bad
a = [1, 2, 3]b = ab[0] = 99print(a) # [99, 2, 3]flowchart LR
subgraph "b = a (참조 공유)"
direction TB
varA1["변수 a"] --> list1["[1, 2, 3]"]
varB1["변수 b"] --> list1
end
subgraph "b = a.copy() (독립 복사)"
direction TB
varA2["변수 a"] --> list2["[1, 2, 3]"]
varB2["변수 b"] --> list3["[1, 2, 3]"]
end Good
a = [1, 2, 3]b = a.copy()b[0] = 99print(a) # [1, 2, 3]11) 자료구조 디버깅 습관
Section titled “11) 자료구조 디버깅 습관”- 자료형부터 확인:
print(type(x), x) - 길이 확인:
print(len(x)) - dict는 key 읽기 전
in또는get()확인 - set은
sorted()로 출력해 보기 - list 복사는
a.copy()로 했는지 확인
예제 1 줄별 해설
Section titled “예제 1 줄별 해설”- list를 만들고 첫 값과 마지막 값을 읽습니다.
- 슬라이싱으로 여러 값을 한 번에 가져옵니다.
- list는 값을 수정하고
append()로 새 값을 추가할 수 있습니다. - tuple은 index로 읽을 수 있지만 수정은 하지 않습니다.
예제 2 줄별 해설
Section titled “예제 2 줄별 해설”sensor는 key와 value로 이루어진 dict입니다.sensor["id"],sensor["temp"]처럼 key 이름으로 값을 읽습니다.sensor["status"] = "warn"은 기존 값 수정입니다.sensor["location"] = "lab-1"은 새 key 추가입니다.get()은 없는 key를 안전하게 읽을 때 씁니다."temp" in sensor는 key 존재 여부를 확인합니다.
예제 3 줄별 해설
Section titled “예제 3 줄별 해설”- 원본은 중복이 있는 list입니다.
set(tags)로 중복을 제거합니다.- set은 순서가 일정하지 않을 수 있으므로
sorted()로 출력합니다. add()로 값을 하나 추가할 수 있습니다.in으로 포함 여부를 확인할 수 있습니다.
예제 4 줄별 해설
Section titled “예제 4 줄별 해설”for temp in temperatures:는 list 원소를 순서대로 꺼냅니다.total += temp로 누적하면 평균 계산 준비가 됩니다.- dict는
items()를 사용하면 key와 value를 함께 꺼낼 수 있습니다. - 이 패턴은 다음 주 파싱 결과를 dict/list로 다룰 때 바로 이어집니다.
자주 나오는 실수 요약표
Section titled “자주 나오는 실수 요약표”| 실수 | 증상 | 이유 | 해결 |
|---|---|---|---|
| tuple을 list처럼 수정 | TypeError | tuple은 수정 불가 | 수정이 필요하면 list 사용 |
| 슬라이싱 끝값 포함으로 착각 | 결과가 한 칸 다름 | 끝 index는 포함되지 않음 | start <= i < end 기억 |
| dict 없는 key 즉시 조회 | KeyError | 방어 코드 없음 | get() 또는 if key in dict |
| set에 순서 기대 | 출력 순서가 달라 보임 | set은 순서형 아님 | sorted(set_data) 사용 |
b = a를 복사로 착각 | 원본까지 바뀜 | 같은 list를 가리킴 | a.copy() 사용 |
실습 문제 · 직접 코딩
Section titled “실습 문제 · 직접 코딩”문제: 점수 5개가 들어 있는 list에서 최고점, 최저점, 평균을 계산해 출력하세요.
해야 할 일
highest,lowest,average변수를 만드세요.- 세 값을 모두 출력하세요.
힌트
max(scores),min(scores),sum(scores),len(scores)를 사용할 수 있습니다.- 평균은
sum(scores) / len(scores)입니다.
문제: 좌표를 나타내는 tuple 3개가 주어졌을 때, 각 tuple의 x값(첫 번째 값)만 한 줄씩 출력하세요.
해야 할 일
point1,point2,point3의 x값을 각각 출력하세요.
힌트
- tuple의 첫 번째 값은 index
0입니다. - 예:
point1[0]
문제: 장비 상태를 저장한 dict에서 특정 장비의 상태를 읽고, 없는 key는 안전하게 처리하세요.
해야 할 일
A02의 상태를 출력하세요.A04의 상태를 읽되, 없으면"미등록"을 출력하세요.A03key가 dict 안에 있는지도 출력하세요.
힌트
- 있는 key는
sensor_status["A02"]처럼 읽을 수 있습니다. - 없는 key는
get()을 쓰는 편이 안전합니다. - 존재 여부는
"A03" in sensor_status로 검사합니다.
문제: 중복된 장비 ID 목록에서 고유한 ID만 남기고, 정렬된 결과와 개수를 출력하세요.
해야 할 일
sensor_ids를 set으로 바꿔 중복을 제거하세요.- 정렬된 고유 ID 목록을 출력하세요.
- 고유 ID 개수도 출력하세요.
힌트
unique_ids = set(sensor_ids)sorted(unique_ids)len(unique_ids)
문제: 장비 상태 dict를 반복문으로 순회해 WARN 상태 장비만 출력하세요.
해야 할 일
- 반복문으로 key와 value를 하나씩 읽으세요.
- 상태가
"WARN"인 장비 ID만 출력하세요. - 마지막에
WARN장비 수를 함께 출력하세요.
힌트
for sensor_id, status in sensor_status.items():- 조건문은
if status == "WARN": - 개수는
count += 1로 누적할 수 있습니다.
문제: 단어 list에서 각 단어가 몇 번 나왔는지 dict로 세어 보세요.
해야 할 일
- 반복문을 사용해
countdict에 단어별 등장 횟수를 저장하세요. count전체를 출력하세요.radar와noise의 횟수를 각각 출력하세요.
힌트
- 단어가 이미 있으면 1 증가시키고, 없으면 1로 시작하면 됩니다.
- 예:
if word in count:
C와 비교: 배열/struct와 Python 자료구조는 이렇게 다릅니다
Section titled “C와 비교: 배열/struct와 Python 자료구조는 이렇게 다릅니다”| 항목 | C | Python | 기억할 점 |
|---|---|---|---|
| 순차 데이터 | 배열 int a[3] | list / tuple | Python은 크기와 타입이 더 유연하다 |
| 레코드 데이터 | struct | dict | Python은 key 문자열로 바로 접근 |
| 중복 제거 | 직접 구현 | set | Python은 집합이 기본 제공 |
| 수정 가능성 | 선언 방식에 따라 다름 | list/dict/set 수정 가능, tuple 불가 | 선택이 문법에 드러난다 |
struct Sensor { char id[10]; double temp;};
struct Sensor s = {"A01", 23.4};printf("%s %.1f\n", s.id, s.temp);sensor = {"id": "A01", "temp": 23.4}print(sensor["id"], sensor["temp"])