6주차 · 자료구조
이번 장에서는 Python의 핵심 4구조를 “처음부터” 익힙니다.
list: 순서 있는 가변 컨테이너tuple: 순서 있는 불변 컨테이너dict: key로 찾는 매핑set: 중복 없는 집합[],(),{},{key: value}문법 차이를 구분할 수 있습니다.- C의 배열/구조체와 Python 자료구조의 차이를 설명할 수 있습니다.
목표는 문법 암기가 아니라, 상황에 맞는 선택 능력입니다.
1) 왜 자료구조가 필요한가?
Section titled “1) 왜 자료구조가 필요한가?”데이터 구조는 “정리 방식”입니다.
flowchart TB A[같은 데이터] --> B[구조를 어떻게 담는가] B --> C[찾는 속도] B --> D[수정 가능성] B --> E[버그 발생률]
예: 학생 이름 100명을 관리할 때
- 순서 출력이 중요하면
list - ID로 즉시 찾으려면
dict - 중복 이름 제거가 목적이면
set
2) 한눈에 비교하기
Section titled “2) 한눈에 비교하기”| 구조 | 순서 유지 | 수정 가능 | 권장 사용 |
|---|---|---|---|
list [ ] | 예 | 예 | 순회/수정/추가 |
tuple ( ) | 예 | 아니오 | 고정값(좌표, 설정) |
dict {k:v} | 예* | 예 | key 기반 빠른 조회 |
set { } | 인덱스 순서 없음 | 예 | 중복 제거, 포함 검사 |
* Python 3.7+ 삽입 순서 유지
자료구조 문법 quickstart: 기호부터 익히기
Section titled “자료구조 문법 quickstart: 기호부터 익히기”자료구조는 이름보다 기호 모양을 먼저 눈에 익히는 것이 중요합니다.
| 구조 | 만드는 문법 | 예시 | 바로 떠올릴 점 |
|---|---|---|---|
list | [ ... ] | [10, 20, 30] | 순서 있고 수정 가능 |
tuple | ( ... ) | (3, 5) | 순서 있고 수정 불가 |
dict | {key: value} | {"id": "A01", "temp": 23.4} | 이름으로 찾기 |
set | { ... } 또는 set() | {"ok", "warn"} | 중복 제거 |
처음 쓸 때 특히 주의할 점
Section titled “처음 쓸 때 특히 주의할 점”dict는 콜론:이 들어간다. ({"id": "A01"})set은 값만 적고 콜론이 없다. ({"ok", "warn"})- 빈 중괄호
{}는dict이다. - 빈
set은 반드시set()으로 만든다.
numbers = [1, 2, 3]point = (3, 5)sensor = {"id": "A01", "temp": 23.4}tags = set()
tags.add("ok")print(numbers[0], point[1], sensor["id"], tags)3) list와 tuple 기초 다지기
Section titled “3) list와 tuple 기초 다지기”list — 인덱스 구조 한눈에 보기
Section titled “list — 인덱스 구조 한눈에 보기”리스트의 각 원소는 **인덱스(번호)**로 접근합니다. 인덱스는 항상 0부터 시작합니다.
values = [12.4, 11.8, 12.9, 13.1, 12.2]| 인덱스 | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 원소 | 12.4 | 11.8 | 12.9 | 13.1 | 12.2 |
| 음수 인덱스 | -5 | -4 | -3 | -2 | -1 |
values[0]→12.4(첫 번째)values[-1]→12.2(마지막)values[1:4]→[11.8, 12.9, 13.1](인덱스 1 이상, 4 미만)
list 연산 단계별 추적
Section titled “list 연산 단계별 추적”values = [12.4, 11.8, 12.9, 13.1, 12.2]values.append(14.0)values[2] = 12.7| 단계 | 연산 | 리스트 상태 |
|---|---|---|
| 초기 | 생성 | [12.4, 11.8, 12.9, 13.1, 12.2] |
| 1 | append(14.0) | [12.4, 11.8, 12.9, 13.1, 12.2, 14.0] |
| 2 | values[2] = 12.7 | [12.4, 11.8, 12.7, 13.1, 12.2, 14.0] |
values = [12.4, 11.8, 12.9, 13.1, 12.2]print(values[0]) # 12.4 첫 값print(values[-1]) # 12.2 마지막 값print(values[1:4]) # [11.8, 12.9, 13.1] 0 포함, 4 제외values[2] = 12.7 # 인덱스 2 수정values.append(14.0) # 맨 끝에 추가print(values)point = (3, 5)print(point[0])# point[0] = 7 # TypeError (수정 불가)초보자 핵심 포인트
Section titled “초보자 핵심 포인트”list는 “변하는 데이터”tuple은 “바뀌면 안 되는 데이터”
4) dict와 set 기초 다지기
Section titled “4) dict와 set 기초 다지기”key로 값을 꺼내는 구조입니다. 존재하지 않는 key를 []로 바로 조회하면 KeyError가 발생하므로, 조회 전에 key가 있는지 먼저 확인하는 습관을 들이세요.
sensor = {"id": "A01", "temp": 23.4}print(sensor["temp"]) # key 조회print(sensor.get("humidity", "N/A")) # 안전 조회 (없으면 기본값 반환)sensor["status"] = "ok" # 추가/수정key 존재 확인 패턴:
if "humidity" in sensor: print(sensor["humidity"])else: print("humidity 없음")tags = {"ok", "warn"}tags.add("offline")print("ok" in tags)print(set(["ok", "ok", "warn"])) # {'ok', 'warn'}- set은 중복 제거와 포함 검사에 특화
- 출력 순서를 기대하면 안 됨 (
sorted()권장)
5) 알고리즘적 사고: 구조 선택 절차
Section titled “5) 알고리즘적 사고: 구조 선택 절차”문제: “센서 상태를 저장하고, ID로 빠르게 조회하며, 상태 중복을 제거하라”
- 원본 레코드 여러 개 →
list[dict] - ID 조회 최적화 →
dict(ID -> row) - 상태값 중복 제거 →
set - 화면 출력 안정화 →
sorted(set_result)
flowchart TB A[records<br/>list of dict] --> B[lookup dict by id] A --> C[status list] C --> D[set 중복 제거] D --> E[sorted list 출력]
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"])핵심은 배열처럼 순서가 중요하면 list/tuple, 구조체처럼 이름으로 찾고 싶으면 dict, 중복 제거가 필요하면 set 입니다.
6) 자료구조 디버깅 습관
Section titled “6) 자료구조 디버깅 습관”- 자료형부터 출력:
print(type(x), x) - 길이 확인:
len(list_or_dict) - key 조회 전 존재 확인:
if key in d: - 슬라이싱 결과 직접 출력:
print(a[1:4]) - 복사 실수 점검:
b = a.copy()사용
7) 잘못된 예와 올바른 예
Section titled “7) 잘못된 예와 올바른 예”A. dict key 안전 조회
Section titled “A. dict key 안전 조회”Bad
lookup = {"A01": 23.4}print(lookup["A03"]) # KeyError 발생Good
# 방법 1: get() 사용print(lookup.get("A03", "missing"))
# 방법 2: in 으로 먼저 확인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] 원본 유지 예제 1 줄별 해설
Section titled “예제 1 줄별 해설”numbers = [12.4, 11.8, 12.9, 13.1, 12.2]— 실수 5개짜리 리스트 생성numbers[0]→12.4,numbers[-1]→12.2— 양수/음수 인덱싱으로 첫/끝 값 조회numbers[1:4]→[11.8, 12.9, 13.1]— 인덱스 1 이상 4 미만 구간 추출numbers[2] = 12.7— 인덱스 2(세 번째 원소12.9)를12.7로 수정numbers.append(14.0)— 리스트 맨 끝에 새 값 추가meta = ("sensor-A", "2026-03-01")— 튜플 생성 후 읽기만 수행 (수정 불가)
예제 2 줄별 해설
Section titled “예제 2 줄별 해설”list[dict]형태로 레코드 저장for루프로lookupdict를 직접 구성 —lookup[row["id"]] = rowif "A02" in lookup:— key 존재를 먼저 확인한 뒤[]조회 (KeyError 방지)lookup.get("A04", "missing")— 없는 key는 기본값 반환set(tags)— 중복 제거sorted()— 출력 안정화
예제 3 줄별 해설
Section titled “예제 3 줄별 해설”if "A02" in lookup:— key가 있는지 먼저 확인 (try/except없이 안전 처리)lookup.get("A02", -1)— 없는 key에 기본값 반환하는 두 번째 안전 방법alias = origin— 참조 복사: alias와 origin이 같은 리스트를 가리킴alias[0] = 99— alias를 수정했는데 origin도 함께 바뀌는 것을 관찰origin.copy()— 독립 복사본 생성 → clone 수정이 origin에 영향 없음
초보자 실수 요약표
Section titled “초보자 실수 요약표”| 실수 | 증상 | 원인 | 수정 방법 |
|---|---|---|---|
| tuple을 list처럼 수정 | TypeError | 불변 자료형 개념 부족 | 수정 필요 데이터는 list 사용 |
| 슬라이싱 끝 인덱스 오해 | 범위가 1칸 어긋남 | 끝 인덱스 제외 규칙 미이해 | start <= i < end 기억 |
| dict 없는 key 즉시 조회 | KeyError | 방어 코드 없음 | get() 또는 if key in dict |
| set 출력 순서 기대 | 실행마다 순서가 달라 보임 | set은 순서형이 아님 | sorted(set_data)로 표시 |
b = a를 깊은 복사로 오해 | 원본까지 변경 | 참조 공유 | a.copy() 또는 list(a) |
실습 문제 · 직접 코딩
Section titled “실습 문제 · 직접 코딩”문제: 점수 리스트의 최고/최저/평균을 구하세요.
문제: 좌표 3개를 tuple로 저장하고 각 좌표의 x값만 출력하세요.
문제: 장비 상태를 dict로 저장하고, 중복된 장비 ID는 set으로 정리하세요.
문제: 학생 성적 dict 리스트에서 평균 이상인 학생 이름만 모아 list로 출력하세요.
문제: 단어 리스트에서 각 단어의 등장 횟수를 dict로 집계하고, 가장 많이 나온 단어를 출력하세요.