Skip to content

Latest commit

 

History

History

README.md

step05_datastructure — 리스트 · 튜플 · 딕셔너리 · 집합

학습 순서 5번. 새로 작성한 챕터입니다.

왜 여기가 중요한가

step01~04 까지 변수 하나에 값 하나를 담았습니다. 하지만 데이터는 언제나 여럿입니다. 학생 100명, 매출 12개월, 로그 백만 줄.

데이터 분석에서 쓰는 DataFrame 은 결국 "딕셔너리를 담은 리스트" 를 다루기 좋게 포장한 것입니다. 이 챕터를 건너뛰면 pandas가 마법처럼 보이고, 마법은 배울 수 없습니다.

학습 목표

  • 리스트를 만들고 인덱싱·슬라이싱한다
  • 리스트를 바꾸는 메서드(append·insert·remove·pop·sort)를 쓴다
  • 바꿀 수 있는 것(리스트)과 없는 것(튜플) 의 차이를 안다
  • 딕셔너리로 "키 → 값" 관계를 다루고, 실제 데이터 모양인 리스트 안의 딕셔너리를 순회한다
  • 집합으로 중복을 제거한다
  • 컴프리헨션으로 반복문을 한 줄로 줄인다

파일 지도

파일 내용
listEx1.py ★ 리스트 생성·인덱싱·슬라이싱·중첩
listEx2.py 리스트 메서드, 정렬, 통계, 복사의 함정
tupleEx.py 튜플, 패킹/언패킹, 값 교환
dictEx1.py ★ 딕셔너리 기본, get(), 키/값/쌍
dictEx2.py ★ 순회, 리스트 안의 딕셔너리, 개수 세기
setEx.py 집합, 중복 제거, 집합 연산
comprehension.py ★ 리스트·딕셔너리·집합 컴프리헨션
test1.py 연습문제 — 성적 데이터 집계
test2.py 연습문제 — 단어 빈도

실행 방법

python3 step05_datastructure/listEx1.py

전부 입력 없이 실행됩니다.

짚고 갈 지점

네 가지를 언제 쓰나

기호 순서 변경 중복 쓰는 곳
리스트 [] ○ ○ ○ 기본값. 대부분 이걸 씁니다
튜플 () ○ × ○ 바뀌면 안 되는 값, 함수의 여러 반환값
딕셔너리 {k:v} ○ ○ 키는 × 이름으로 찾을 때
집합 {} × ○ × 중복 제거, 포함 여부 검사

슬라이싱은 range 와 규칙이 같다

nums = [10, 20, 30, 40, 50]
nums[1:3]     # [20, 30]   ← 3번은 포함 안 됨
nums[:3]      # 처음부터
nums[2:]      # 끝까지
nums[::-1]    # 뒤집기
nums[-1]      # 마지막

끝을 포함하지 않는다는 규칙이 range·슬라이싱·randrange 에 일관되게 적용됩니다. (randint 만 예외였죠.)

[::-1] 로 뒤집는 건 관용구처럼 자주 씁니다. 문자열에도 됩니다 — 'korea'[::-1] 은 'aerok'.

복사의 함정 — 반드시 짚고 가세요

a = [1, 2, 3]
b = a               # 복사가 아니다! 같은 리스트에 이름표를 하나 더 붙인 것
b.append(4)
print(a)            # [1, 2, 3, 4]  ← a 도 바뀐다

파이썬에서 = 는 값을 베끼는 게 아니라 같은 것을 가리키게 합니다. 리스트처럼 내용을 바꿀 수 있는 자료형에서 이게 사고를 냅니다.

d = c[:]            # 슬라이싱으로 복사
d = c.copy()        # 또는 이렇게

listEx2.py 에서 두 경우를 나란히 실행해 확인하세요. 초보자가 가장 오래 헤매는 버그입니다.

dict.get() 은 이미 만난 적이 있습니다

student.get('주소')            # 없으면 None (KeyError 대신)
student.get('주소', '미입력')   # 없으면 기본값

step03_controlstmt/switchex/switchEx.py 의 .get(x, 9) 가 바로 이것이었습니다. 그때는 "switch 흉내"로만 봤지만, 실은 딕셔너리에서 안전하게 값을 꺼내는 표준 방법입니다.

실제 데이터는 "리스트 안의 딕셔너리"입니다

students = [
    {'이름': '홍길동', '국어': 85, '영어': 73},
    {'이름': '김길동', '국어': 90, '영어': 88},
]

for s in students:
    print(s['이름'], s['국어'] + s['영어'])

CSV 파일, JSON API 응답, 데이터베이스 조회 결과 — 거의 모든 실제 데이터가 이 모양으로 파이썬에 도착합니다. step08 에서 CSV를 읽으면 정확히 이 구조가 나오고, pandas의 DataFrame 도 이걸 표로 포장한 것입니다.

dictEx2.py 를 가장 여러 번 읽으세요.

개수 세기 관용구

counter = {}
for c in text:
    counter[c] = counter.get(c, 0) + 1

"없으면 0에서 시작해서 1 더한다". 빈도 계산에서 계속 나오는 패턴입니다. 표준 라이브러리에 collections.Counter 라는 전용 도구도 있는데, step09 에서 만납니다.

컴프리헨션 읽는 순서

[i ** 2 for i in range(1, 6)]
#  ↑ 넣을 값   ↑ 반복

뒤부터 읽으세요. "range(1,6)에서 i를 하나씩 꺼내서, i의 제곱을 넣는다".

조건이 붙으면:

[i for i in range(1, 11) if i % 2 == 0]     # 걸러내기 — if 가 뒤
['합격' if s >= 60 else '불합격' for s in scores]   # 값 고르기 — if 가 앞

if 위치가 다릅니다. 뒤에 오면 "거를지 말지", 앞에 오면 "어떤 값을 넣을지"(3항 연산자)입니다. step03 에서 배운 3항 연산자가 여기서 다시 나옵니다.

컴프리헨션은 짧아서 쓰는 것입니다. 한 줄이 길어지거나 조건이 여러 개면 그냥 for 문이 읽기 좋습니다.

집합이 빠른 이유

99999 in big_list    # 10만 번 비교 — 느리다
99999 in big_set     # 위치를 계산해서 한 번에 — 빠르다

setEx.py 마지막 예제입니다. 데이터가 많을 때 "이 값이 있나?"를 자주 물어야 한다면 집합으로 바꿔 두는 것만으로 프로그램이 훨씬 빨라집니다.

흔한 실수

증상 원인
리스트를 바꿨는데 원본도 바뀜 b = a 는 복사가 아님 → b = a[:]
KeyError 없는 키를 [] 로 꺼냄 → .get() 사용
TypeError: 'tuple' object does not support item assignment 튜플은 바꿀 수 없음
{} 가 집합이 아님 빈 집합은 set(). {} 는 빈 딕셔너리
sort() 결과가 None sort() 는 원본을 바꾸고 아무것도 안 돌려줌. 새 리스트가 필요하면 sorted()

마지막 것이 특히 자주 나옵니다.

nums = [3, 1, 2].sort()    # None!
nums = sorted([3, 1, 2])   # [1, 2, 3]

연습 과제

  1. listEx2.py 의 복사 함정 예제에서 b = a 를 b = a[:] 로 바꿔 결과가 달라지는 것을 확인하세요.
  2. 리스트 [5, 3, 8, 1, 9, 2] 에서 sort() 를 쓰지 않고 최댓값을 찾는 코드를 for 로 작성하세요.
  3. test1.py 의 과목별 평균 계산을 컴프리헨션 없이 for 문으로 다시 쓰세요. 어느 쪽이 읽기 좋나요?
  4. test1.py 에 석차를 추가하세요. 평균이 높은 순으로 1등, 2등, 3등을 출력합니다.
  5. test2.py 의 단어 빈도에서 3글자 이하 단어는 제외하도록 고치세요.
  6. 두 학급의 학생 이름 리스트가 있을 때, 집합 연산으로 ① 양쪽 모두에 있는 이름 ② 한쪽에만 있는 이름을 구하세요.
  7. step04_loop/test1.py 의 "1~100 중 3의 배수 개수"를 컴프리헨션 한 줄로 다시 쓰세요.