06. 리스트·튜플·세트·딕셔너리

🎯 이 장의 목표
  • 여러 값을 묶어 담는 네 가지 그릇(리스트·튜플·세트·딕셔너리)을 구분한다.
  • 각 자료구조를 만들고, 값을 꺼내고·추가·수정·삭제한다.
  • 가변(mutable)과 불변(immutable)의 차이를 이해한다.
  • 상황에 맞게 "무엇을 쓸지" 고를 수 있다.

왜 여러 값을 묶을까

지금까진 변수 하나에 값 하나를 담았습니다. 그런데 "반 학생 30명의 이름"을 변수 30개로 관리한다면 끔찍하겠죠. 여러 값을 하나로 묶는 그릇이 필요합니다.

Python의 대표적인 그릇은 네 가지이고, 각각 성격이 다릅니다. 먼저 전체 지도를 봅시다.

flowchart TD
    Root["여러 값을 담는 그릇"]:::proc

    Root --> List["📋 리스트 list<br/>[ ]<br/>순서 O · 수정 O · 중복 O"]:::data
    Root --> Tuple["📌 튜플 tuple<br/>( )<br/>순서 O · 수정 X · 중복 O"]:::data
    Root --> Set["🎯 세트 set<br/>{ }<br/>순서 X · 수정 O · 중복 X"]:::data
    Root --> Dict["🗂️ 딕셔너리 dict<br/>{키:값}<br/>키로 조회 · 수정 O"]:::result

    classDef proc fill:#7fd8d8,stroke:#2a9d8f,color:#14532d
    classDef data fill:#a8dadc,stroke:#457b9d,color:#1d3557
    classDef result fill:#b8e6c1,stroke:#34a853,color:#14532d

한눈에 비교하면 이렇습니다.

그릇기호순서수정 가능?중복 허용?한 줄 요약
리스트 list[ ]있음✅ 가능가장 많이 쓰는 만능 목록
튜플 tuple( )있음❌ 불가바뀌면 안 되는 묶음
세트 set{ }없음✅ 가능중복 없는 모음
딕셔너리 dict{키:값}있음✅ 가능키 중복 ❌이름표(키)로 찾는 사전

📋 리스트 (list)

가장 많이 쓰는 자료구조입니다. 대괄호 [ ]에 값을 쉼표로 나열합니다. 장바구니에 물건을 순서대로 담는다고 생각하면 됩니다.

PYTHON
fruits = ["사과", "배", "감"]
print(fruits)        # ['사과', '배', '감']
print(len(fruits))   # 3

값 꺼내기 (인덱싱·슬라이싱)

문자열과 똑같이 0부터 시작하는 인덱스로 꺼냅니다.

PYTHON
fruits = ["사과", "배", "감"]
print(fruits[0])     # 사과   (첫 번째)
print(fruits[-1])    # 감     (마지막)
print(fruits[0:2])   # ['사과', '배']   (슬라이싱, 끝 미포함)
💡 팁
4장에서 배운 인덱싱·슬라이싱 규칙이 리스트에도 똑같이 적용됩니다. "0부터, 끝은 미포함"은 Python 전체를 관통하는 약속입니다.

추가·수정·삭제

리스트는 수정 가능(mutable)합니다. 만든 뒤에도 내용을 바꿀 수 있습니다.

PYTHON
fruits = ["사과", "배", "감"]

fruits.append("귤")        # 맨 뒤에 추가
print(fruits)              # ['사과', '배', '감', '귤']

fruits[0] = "딸기"          # 0번 값 수정
print(fruits)              # ['딸기', '배', '감', '귤']

fruits.remove("감")        # 값으로 삭제
print(fruits)              # ['딸기', '배', '귤']

자주 쓰는 리스트 메서드입니다.

메서드하는 일
.append(x)맨 뒤에 x 추가
.insert(i, x)i번 위치에 x 삽입
.remove(x)값이 x인 첫 항목 삭제
.pop()맨 뒤 항목을 꺼내며 삭제
.sort()정렬 (원본을 바꿈)
.reverse()순서 뒤집기
x in 리스트포함 여부 (True/False)
PYTHON
nums = [3, 1, 2]
nums.sort()
print(nums)          # [1, 2, 3]
print(2 in nums)     # True
⚠️ 흔한 실수
흔한 함정: 없는 인덱스에 접근하면 에러가 납니다.
```python
fruits = ["사과", "배"]
print(fruits[5])
```
```text
IndexError: list index out of range
```
IndexError는 "그 번호는 범위 밖이다"라는 뜻입니다. 항목이 2개면 유효한 인덱스는 0과 1뿐입니다.

📌 튜플 (tuple)

튜플은 수정할 수 없는 리스트라고 생각하면 됩니다. 소괄호 ( )로 만듭니다.

PYTHON
point = (3, 5)
print(point[0])     # 3
print(point[1])     # 5

리스트와 거의 같지만, 한 번 만들면 바꿀 수 없습니다(불변, immutable).

PYTHON
point = (3, 5)
point[0] = 9        # ❌ 에러!
TEXT
TypeError: 'tuple' object does not support item assignment

"바꿀 수 없는 게 무슨 장점이지?"싶을 수 있습니다. 바로 그 점이 장점입니다. 실수로 바뀌면 안 되는 값(좌표, 색상 RGB값, 요일 등)을 튜플로 만들면 안전합니다.

PYTHON
RGB_RED = (255, 0, 0)     # 빨강은 영원히 (255, 0, 0)
WEEKDAYS = ("월", "화", "수", "목", "금")
📌 핵심
핵심: 바뀔 목록은 리스트, 바뀌면 안 되는 묶음은 튜플. 불변은 제약이 아니라 안전장치다.

언패킹: 튜플의 진짜 매력

튜플(과 리스트)은 여러 변수에 한 번에 풀어 담을 수 있습니다. 이를 언패킹(unpacking)이라 합니다.

PYTHON
point = (3, 5)
x, y = point        # x에 3, y에 5가 한 번에!
print(x, y)         # 3 5

이 문법 덕분에 두 변수의 값을 맞바꾸는 것도 한 줄로 끝납니다 — 아주 파이썬다운 표현입니다.

PYTHON
a = 1
b = 2
a, b = b, a         # 값을 동시에 맞바꿈
print(a, b)         # 2 1
💡 팁
다른 언어에서는 임시 변수를 거쳐야 하는 "값 교환"이 Python에서는 a, b = b, a 한 줄입니다. 8장 반복문에서 이 언패킹이 또 등장합니다.

🎯 세트 (set)

세트는 중복이 없는 모음입니다. 중괄호 { }로 만듭니다. 순서가 없어서 인덱스로 꺼낼 수는 없습니다.

PYTHON
nums = {1, 2, 2, 3, 3, 3}
print(nums)         # {1, 2, 3}   ← 중복이 저절로 사라짐!

세트의 가장 흔한 쓰임: 중복 제거

리스트에서 중복을 없애고 싶을 때, set()으로 바꿨다가 다시 list()로 되돌리는 것이 정석입니다.

PYTHON
dup = [1, 1, 2, 3, 3, 3]
unique = list(set(dup))
print(unique)       # [1, 2, 3]

집합 연산

수학의 집합처럼 교집합·합집합·차집합을 구할 수 있습니다.

PYTHON
a = {1, 2, 3}
b = {2, 3, 4}
print(a & b)        # {2, 3}        교집합 (둘 다 있는 것)
print(a | b)        # {1, 2, 3, 4}  합집합 (전부 합치기)
print(a - b)        # {1}           차집합 (a에만 있는 것)
flowchart LR
    subgraph A["세트 a"]
        direction TB
        A1["1"]
    end
    subgraph Both["교집합 a & b"]
        I1["2, 3"]
    end
    subgraph B["세트 b"]
        B1["4"]
    end
    A -.합집합 a or b.-> Both -.-> B

    classDef default fill:#a8dadc,stroke:#457b9d,color:#1d3557
연산기호의미
교집합a & b양쪽 모두에 있는 원소
합집합`a \b`
차집합a - ba에는 있고 b에는 없는 원소
💡 팁
"두 그룹의 공통 친구 찾기", "A는 했고 B는 안 한 일" 같은 문제에 세트 연산이 딱입니다. 포함 여부(in) 확인도 리스트보다 세트가 훨씬 빠릅니다.

🗂️ 딕셔너리 (dict)

딕셔너리는 이름 그대로 사전입니다. 단어(키)를 찾으면 뜻(값)이 나오죠. 키(key)와 값(value)을 짝지어 저장합니다.

PYTHON
person = {
    "name": "민지",
    "age": 25,
    "city": "서울",
}
print(person["name"])   # 민지   ← 키로 값을 꺼냄
print(person["age"])    # 25

리스트가 번호(인덱스)로 값을 찾는다면, 딕셔너리는 이름(키)으로 값을 찾습니다. "0번, 1번"보다 "name, age"가 훨씬 의미가 분명하죠.

추가·수정·삭제·조회

PYTHON
person = {"name": "민지", "age": 25}

person["age"] = 26          # 기존 키 → 수정
person["city"] = "서울"      # 새 키 → 추가
print(person)               # {'name': '민지', 'age': 26, 'city': '서울'}

print("name" in person)     # True   ← 키가 있는지 확인
⚠️ 흔한 실수
흔한 함정: 없는 키를 대괄호로 꺼내면 에러가 납니다.
```python
print(person["phone"])
```
```text
KeyError: 'phone'
```
KeyError는 "그런 키가 없다"는 뜻입니다. 이를 피하려면 .get()을 쓰세요. .get()은 키가 없으면 에러 대신 기본값을 돌려줍니다.
```python
print(person.get("phone", "없음")) # 없음 (에러 안 남)
```

키·값 전체 다루기

PYTHON
person = {"name": "민지", "age": 26}
print(person.keys())     # dict_keys(['name', 'age'])
print(person.values())   # dict_values(['민지', 26])
print(person.items())    # dict_items([('name', '민지'), ('age', 26)])

.items()는 8장 반복문에서 "키와 값을 동시에 훑는" 용도로 다시 만납니다. (앞서 본 언패킹과 결합됩니다!)

PYTHON
for key, value in person.items():
    print(f"{key} → {value}")
# name → 민지
# age → 26

가변(mutable) vs 불변(immutable) 정리

이 장에서 계속 나온 핵심 개념을 정리합시다. 만든 뒤 내용을 바꿀 수 있으면 가변, 못 바꾸면 불변입니다.

자료형가변/불변
리스트 list가변 (mutable)
딕셔너리 dict가변
세트 set가변
튜플 tuple불변 (immutable)
문자열 str불변
숫자 int/float불변
⚠️ 흔한 실수
알아두면 좋은 점: 문자열도 불변입니다. 그래서 s.upper()는 원본 s를 바꾸지 않고 새 문자열을 돌려줍니다. 결과를 쓰려면 s = s.upper()처럼 다시 담아야 합니다. (가변/불변의 더 깊은 동작은 중급편에서 다룹니다.)

무엇을 언제 쓸까? (선택 가이드)

flowchart TD
    Q1{"키(이름)로<br/>값을 찾고 싶다?"}:::proc
    Q1 -->|예| Dict["🗂️ 딕셔너리"]:::result
    Q1 -->|아니오| Q2{"중복을 자동으로<br/>없애고 싶다?"}:::proc
    Q2 -->|예| Set["🎯 세트"]:::result
    Q2 -->|아니오| Q3{"나중에 내용이<br/>바뀔 수 있다?"}:::proc
    Q3 -->|예| List["📋 리스트"]:::result
    Q3 -->|아니오, 고정| Tuple["📌 튜플"]:::result

    classDef proc fill:#fff3b0,stroke:#e0a800,color:#5c4500
    classDef result fill:#b8e6c1,stroke:#34a853,color:#14532d
  • 리스트: 가장 기본. 순서가 있고 바뀌는 목록 (할 일 목록, 점수들)
  • 튜플: 바뀌면 안 되는 고정된 묶음 (좌표, RGB)
  • 세트: 중복 없는 모음, 빠른 포함 검사 (방문한 페이지, 태그)
  • 딕셔너리: 이름표로 찾는 데이터 (사용자 정보, 설정값)

이 장에서 배운 것

  • 여러 값을 담는 네 그릇: 리스트[]·튜플()·세트{}·딕셔너리{키:값}.
  • 리스트는 가장 만능. .append()·[i]=·.remove()로 추가·수정·삭제하고 인덱싱·슬라이싱으로 꺼낸다.
  • 튜플은 불변이라 안전하다. 언패킹(x, y = point, a, b = b, a)이 강력하다.
  • 세트는 중복 자동 제거와 집합 연산(&, |, -)에 쓴다.
  • 딕셔너리는 키로 값을 조회한다. 없는 키는 KeyError, 안전하게는 .get(키, 기본값).
  • 리스트·딕셔너리·세트는 가변, 튜플·문자열·숫자는 불변이다.

🧪 실습 문제

문제 1. 리스트 colors = ["빨강", "초록", "파랑"]"노랑"을 맨 뒤에 추가하고, "초록"을 삭제한 뒤 전체를 출력하세요.

문제 2. 다음 코드의 출력은? 왜 그런지도 설명해 보세요.

PYTHON
nums = [5, 3, 5, 1, 3, 5]
print(len(set(nums)))

문제 3. 딕셔너리 book = {"title": "파이썬 입문", "pages": 300}에서 (a) "title" 값을 출력하고, (b) .get()을 이용해 없는 키 "author""미상"이라는 기본값으로 출력하세요.

문제 4. 좌표 튜플 pos = (10, 20)을 언패킹해 x, y에 각각 담은 뒤 x좌표=10, y좌표=20 형태로 출력하세요.

문제 5. (생각해보기) 다음 중 튜플로 만드는 게 더 적절한 것은? 리스트가 더 적절한 것은?

  • (a) 한 주의 요일 이름들
  • (b) 사용자가 장바구니에 담는 상품들
  • (c) 화면 해상도 (1920, 1080)

<details>

<summary>✅ 정답·해설 보기</summary>

1.

PYTHON
colors = ["빨강", "초록", "파랑"]
colors.append("노랑")
colors.remove("초록")
print(colors)   # ['빨강', '파랑', '노랑']

2. 3. set(nums)이 중복을 없애 {1, 3, 5}(원소 3개)가 되고, len()이 3을 반환합니다. "서로 다른 값이 몇 개인가"를 셀 때 쓰는 흔한 기법입니다.

3.

PYTHON
book = {"title": "파이썬 입문", "pages": 300}
print(book["title"])               # 파이썬 입문
print(book.get("author", "미상"))   # 미상

4.

PYTHON
pos = (10, 20)
x, y = pos
print(f"x좌표={x}, y좌표={y}")   # x좌표=10, y좌표=20

5.

  • (a) 튜플 — 요일은 고정되어 바뀌지 않음
  • (b) 리스트 — 담고 빼는 등 계속 바뀜
  • (c) 튜플 — 해상도는 하나의 고정된 묶음

</details>

◀️ 이전 장: 05. 불리언과 비교 | ▶️ 다음 장: 07. 조건문