프로그래밍 언어에서 자료구조(data structure)는 데이터를 효율적으로 저장·조작하는 방법을 뜻함. 이번 5장에서는 리스트·튜플 등을 더 깊이 살펴보고, 집합(set)·딕셔너리(dict) 같은 자료구조와, 루프 테크닉·조건문 활용 같은 다양한 기법을 다룸.
1 리스트 더 알아보기 (More on Lists)
리스트: 다양한 타입의 값을 순서대로 저장·추가·삭제·정렬 가능한 가변 시퀀스 구조임
1) 리스트 메서드
리스트 메서드: 리스트의 요소 추가·삭제·검색·정렬 등을 수행하는 내장 함수 모음임
- append(x), extend(iterable), insert(i, x)
- remove(x), pop([i]), clear()
- index(x[, start[, end]]), count(x)
- sort(*, key=None, reverse=False), reverse(), copy()
fruits = ['orange', 'apple', 'pear', 'banana', 'kiwi', 'apple', 'banana'] print(fruits.count('apple')) # 2 print(fruits.index('banana')) # 3 fruits.reverse() fruits.append('grape') fruits.sort() print(fruits.pop()) # 'pear'
인사이트
- 가변 메서드는 in-place 동작, 반환값 없음
- 앞쪽 삽입/삭제 O(n), 끝쪽 삽입/삭제 O(1)
2) 스택으로 사용 (Using Lists as Stacks)
스택: 마지막에 들어온 항목을 먼저 꺼내는 LIFO(Last-In, First-Out) 구조임
stack = [3, 4, 5] stack.append(6) # push stack.append(7) print(stack.pop()) # pop -> 7 print(stack) # [3, 4, 5, 6]
인사이트
- 별도 스택 자료구조 없이 리스트로 충분
- 메모리 지역성(locality) 유리
인공지능 분야 활용 예
- DFS(깊이 우선 탐색): 트리 기반 분류, 게임 AI 등
- 백트래킹: 퍼즐 해결, 경로 탐색 등
- 재귀 구조를 반복문으로 대체할 때
- 강화학습 등에서 최신 상태 우선 처리
특히 파이썬에서는 재귀 제한이 있기 때문에, 스택으로 상태를 수동 관리하는 게 안정적임.
from collections import deque queue = deque(["Eric", "John", "Michael"]) queue.append("Terry") queue.append("Graham") print(queue.popleft()) # 'Eric' print(queue) # deque(['Michael','Terry','Graham'])
→ RecursionError: maximum recursion depth exceeded
3) 큐로 사용 (Using Lists as Queues)
큐: 먼저 들어온 항목을 먼저 꺼내는 FIFO 구조임
from collections import deque queue = deque(["Eric", "John", "Michael"]) queue.append("Terry") queue.append("Graham") print(queue.popleft()) # 'Eric' print(queue) # deque(['Michael','Terry','Graham'])
인사이트
- 리스트에서 pop(0)은 O(n): 앞 요소 다 밀어야 함 → 비효율
- deque.popleft()는 O(1): 앞뒤 삽입·삭제 모두 빠름
- 스레드 간 공유, 이벤트 처리, 순차처리에 최적
인공지능 분야 활용 예
- 데이터 버퍼링: 센서 데이터, 로그, 시계열 데이터를 순차적으로 처리
- 경험 리플레이 버퍼 (강화학습): 오래된 경험부터 순차적으로 학습
- BFS(너비 우선 탐색): 그래프 탐색, 최단 경로 찾기 등
- 배치 처리 대기열: 훈련 샘플, 작업 요청 등을 순서대로 처리할 때
4) 리스트 컴프리헨션 (List Comprehensions)
컴프리헨션: 기존 시퀀스를 간결하게 순회·필터링·매핑해 새 리스트를 생성하는 문법임
squares = [x**2 for x in range(10)] positives = [x for x in range(10) if x % 2 == 0]
인사이트
- 가독성과 성능의 최적 균형
- ㅍ데이터 전처리에 자주 활용
5) 중첩 리스트 컴프리헨션 (Nested)
중첩 컴프리헨션: 리스트 컴프리헨션 내부에 또 다른 컴프리헨션을 포함한 형태임
matrix = [ [1,2,3,4], [5,6,7,8], [9,10,11,12], ] # 전치(transpose) transposed = [[row[i] for row in matrix] for i in range(4)] # 또는 transposed = list(zip(*matrix))
2. del 문 (The delfstatement)
del: 변수나 자료구조의 요소를 삭제해 이름-객체 바인딩을 해제하는 명령문임
a = [1,2,3,4,5] del a[0] # [2,3,4,5] del a[1:3] # [2,5] del a # NameError 이후 발생
3. 튜플과 시퀀스 (Tuples and Sequences)
튜플: 한 번 생성하면 변경할 수 없는(immutable) 순서형 데이터 모음임
- 생성: (1,2,3) 또는 1,2,3
- 특징: 수정 불가 → 딕셔너리 키·세트의 원소로 사용 가능
- 패킹/언패킹:
t = 123, 456, 'abc' x, y, z = t
- 빈 튜플: ()
- 하나짜리 튜플: ('hello',)
인사이트
- 이질 데이터 묶음에 적합
student = ("홍길동", 21, True)
- 변경 불가 → 데이터 안전 보장
4. 집합 (Sets)
집합: 중복 없는 요소들의 모음으로, 빠른 멤버십 테스트·집합 연산을 지원함
basket = {'apple','orange','pear','banana'} a = set('abracadabra') b = set('alacazam') print(a - b, a | b, a & b, a ^ b) # 차집합, 합집합, 교집합, 대칭차집합
- 세트 컴프리헨션: {x for x in iterable if cond}
인사이트
- 데이터 유일성 확보
- 텍스트 코퍼스 처리·중복 제거에 유용
5. 딕셔너리 (Dictionaries)
딕셔너리: 키(key)와 값(value) 쌍의 매핑(mapping) 구조임
- 생성: {'jack':4098,'sape':4139} 또는 dict(jack=4098,sape=4139)
- 키는 불변 타입만 사용 가능, 삽입 순서 유지(3.7+)
- 기본 연산:
tel = {'jack':4098,'sape':4139} tel['guido'] = 4127 del tel['sape'] 'jack' in tel
- 딕셔너리 컴프리헨션:
{x: x**2 for x in (2,4,6)}
인사이트
- 하이퍼파라미터·레이블 매핑에 활용
- 해시 기반 빠른 조회 구조
6. 반복 기법들 (Looping Techniques)
반복 기법: 다양한 내장 함수를 활용해 시퀀스·매핑을 효율적으로 순회하는 방식임
- for k,v in dict.items()
- for i,v in enumerate(seq)
- for x,y in zip(seq1,seq2)
- reversed(), sorted()
- in-place 수정 피하기:
import math data = [56.2, float('nan'), 51.7] filtered = [v for v in data if not math.isnan(v)]
7. 조건 더 알아보기 (More on Conditions)
조건식: 비교·논리 연산을 결합해 코드 흐름을 제어함
- 비교 연산자: <, >, ==, !=, in, is
- 연속 비교(체이닝): a < b == c
- 논리 연산자: and, or, not (단락 평가)
- 바다코끼리 연산자(:=, Python 3.8+):
if (n := len(data)) > 10: print(n)
인사이트
- 기본값 지정(x or default)
- 중복 계산 방지
8. 시퀀스 및 기타 타입 비교 (Comparing Sequences and Other Types)
시퀀스 비교: 삽입 순서로 요소별 비교함
(1,2,3) < (1,2,4) # True [1,2,3] < [1,2,4] # True 'ABC' < 'Pascal' < 'Python' # True (1,2,3) == (1.0,2.0,3.0) # True
- 시퀀스 길이 차이는 짧은 쪽이 더 작음
- 다른 타입 < 비교 시 TypeError 발생 (단, int vs float는 예외)
인사이트
- 자연어 부등식과 유사한 직관 설계
- 오류를 숨기지 않아 안정성 높음