들어가며
자료구조는 데이터를 효율적으로 저장하고 관리하는 방법입니다. Python에서 제공하는 주요 자료구조(리스트, 튜플, 딕셔너리, 집합)를 정확히 이해하면 데이터를 더 효율적으로 다룰 수 있습니다.
리스트 (List)
예제 1: 기본 리스트 연산
# 리스트 생성
numbers = [1, 2, 3, 4, 5]
mixed = [1, "hello", 3.14, True]
empty = []
print(f"숫자 리스트: {numbers}")
print(f"혼합 리스트: {mixed}")
print(f"빈 리스트: {empty}")
# 인덱싱과 슬라이싱
print(f"\n인덱싱:")
print(f"첫 번째 요소: {numbers[0]}")
print(f"마지막 요소: {numbers[-1]}")
print(f"\n슬라이싱:")
print(f"인덱스 1~3: {numbers[1:4]}")
print(f"처음부터 3번째까지: {numbers[:3]}")
print(f"2번째부터 끝까지: {numbers[2:]}")
출력:
숫자 리스트: [1, 2, 3, 4, 5]
혼합 리스트: [1, 'hello', 3.14, True]
빈 리스트: []
인덱싱:
첫 번째 요소: 1
마지막 요소: 5
슬라이싱:
인덱스 1~3: [2, 3, 4]
처음부터 3번째까지: [1, 2, 3]
2번째부터 끝까지: [3, 4, 5]
예제 2: 리스트 수정
fruits = ["apple", "banana", "cherry"]
# 요소 추가
fruits.append("date")
print(f"append 후: {fruits}")
# 특정 위치에 삽입
fruits.insert(1, "blueberry")
print(f"insert 후: {fruits}")
# 요소 제거
fruits.remove("banana")
print(f"remove 후: {fruits}")
# 특정 인덱스 제거
removed = fruits.pop(0)
print(f"pop 후: {fruits}, 제거된 요소: {removed}")
# 전체 비우기
fruits_copy = fruits.copy()
fruits_copy.clear()
print(f"clear 후: {fruits_copy}")
출력:
append 후: ['apple', 'banana', 'cherry', 'date']
insert 후: ['apple', 'blueberry', 'banana', 'cherry', 'date']
remove 후: ['apple', 'blueberry', 'cherry', 'date']
pop 후: ['blueberry', 'cherry', 'date'], 제거된 요소: apple
clear 후: []
예제 3: 리스트 메서드
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
print(f"원본: {numbers}")
print(f"길이: {len(numbers)}")
print(f"최댓값: {max(numbers)}")
print(f"최솟값: {min(numbers)}")
print(f"합계: {sum(numbers)}")
# 정렬
sorted_asc = sorted(numbers)
print(f"오름차순 정렬: {sorted_asc}")
sorted_desc = sorted(numbers, reverse=True)
print(f"내림차순 정렬: {sorted_desc}")
# 개수 세기
count = numbers.count(1)
print(f"1의 개수: {count}")
# 인덱스 찾기
index = numbers.index(5)
print(f"5의 인덱스: {index}")
출력:
원본: [3, 1, 4, 1, 5, 9, 2, 6]
길이: 8
최댓값: 9
최솟값: 1
합계: 31
오름차순 정렬: [1, 1, 2, 3, 4, 5, 6, 9]
내림차순 정렬: [9, 6, 5, 4, 3, 2, 1, 1]
1의 개수: 2
5의 인덱스: 4
튜플 (Tuple)
예제 4: 튜플의 특성
# 튜플 생성
coordinates = (10, 20)
colors = ("red", "green", "blue")
single = (42,) # 원소 1개일 때 쉼표 필수
print(f"좌표: {coordinates}")
print(f"색상: {colors}")
print(f"단일 요소: {single}")
# 튜플은 불변
print(f"\n길이: {len(colors)}")
print(f"첫 번째: {colors[0]}")
print(f"슬라이싱: {colors[1:]}")
# 튜플 언팩킹
x, y = coordinates
print(f"\n언팩킹: x={x}, y={y}")
r, g, b = colors
print(f"색상 언팩킹: R={r}, G={g}, B={b}")
출력:
좌표: (10, 20)
색상: ('red', 'green', 'blue')
단일 요소: (42,)
길이: 3
첫 번째: red
슬라이싱: ('green', 'blue')
언팩킹: x=10, y=20
색상 언팩킹: R=red, G=green, B=blue
딕셔너리 (Dictionary)
예제 5: 기본 딕셔너리
# 딕셔너리 생성
student = {
"name": "Alice",
"age": 20,
"grade": "A",
"major": "CS"
}
print(f"학생 정보: {student}")
# 값 접근
print(f"\n이름: {student['name']}")
print(f"학년: {student['grade']}")
# get 메서드 (키가 없으면 None 반환)
phone = student.get('phone')
print(f"전화번호: {phone}")
print(f"전화번호 (기본값): {student.get('phone', 'N/A')}")
# 키와 값
print(f"\n모든 키: {student.keys()}")
print(f"모든 값: {student.values()}")
print(f"키-값 쌍: {student.items()}")
출력:
학생 정보: {'name': 'Alice', 'age': 20, 'grade': 'A', 'major': 'CS'}
이름: Alice
학년: A
전화번호: None
전화번호 (기본값): N/A
모든 키: dict_keys(['name', 'age', 'grade', 'major'])
모든 값: dict_values(['Alice', 20, 'A', 'CS'])
키-값 쌍: dict_items([('name', 'Alice'), ('age', 20), ('grade', 'A'), ('major', 'CS')])
예제 6: 딕셔너리 수정
person = {"name": "Bob", "age": 25}
# 값 추가/수정
person["email"] = "bob@example.com"
person["age"] = 26
print(f"수정 후: {person}")
# 여러 항목 추가
person.update({"city": "Seoul", "job": "Engineer"})
print(f"update 후: {person}")
# 키 제거
del person["city"]
print(f"del 후: {person}")
# pop을 사용한 제거
job = person.pop("job")
print(f"pop 후: {person}, 제거된 값: {job}")
출력:
수정 후: {'name': 'Bob', 'age': 26, 'email': 'bob@example.com'}
update 후: {'name': 'Bob', 'age': 26, 'email': 'bob@example.com', 'city': 'Seoul', 'job': 'Engineer'}
del 후: {'name': 'Bob', 'age': 26, 'email': 'bob@example.com', 'job': 'Engineer'}
pop 후: {'name': 'Bob', 'age': 26, 'email': 'bob@example.com'}, 제거된 값: Engineer
집합 (Set)
예제 7: 기본 집합
# 집합 생성
fruits = {"apple", "banana", "cherry", "apple"}
numbers = {1, 2, 3, 2, 1}
print(f"과일: {fruits}") # 중복 제거됨
print(f"숫자: {numbers}")
# 빈 집합은 set() 사용
empty_set = set()
print(f"빈 집합: {empty_set}")
# 집합 연산
set_a = {1, 2, 3, 4}
set_b = {3, 4, 5, 6}
print(f"\n합집합: {set_a | set_b}")
print(f"교집합: {set_a & set_b}")
print(f"차집합: {set_a - set_b}")
print(f"대칭차: {set_a ^ set_b}")
출력:
과일: {'apple', 'banana', 'cherry'}
숫자: {1, 2, 3}
빈 집합: set()
합집합: {1, 2, 3, 4, 5, 6}
교집합: {3, 4}
차집합: {1, 2}
대칭차: {1, 2, 5, 6}
흔한 실수와 해결책
실수 1: 튜플 수정 시도
틀린 예제:
t = (1, 2, 3)
t[0] = 10 # TypeError!
올바른 예제:
t = (1, 2, 3)
t = (10, 2, 3) # 새 튜플로 재할당
print(t) # (10, 2, 3)
실수 2: 리스트 얕은 복사
틀린 예제:
list1 = [1, 2, 3]
list2 = list1 # 참조만 복사
list2.append(4)
print(list1) # [1, 2, 3, 4] - 원본도 변경됨!
올바른 예제:
list1 = [1, 2, 3]
list2 = list1.copy() # 깊은 복사
list2.append(4)
print(list1) # [1, 2, 3]
print(list2) # [1, 2, 3, 4]
실수 3: 딕셔너리 키 타입 주의
틀린 예제:
d = {1: "one", "1": "one"}
print(len(d)) # 2개 (정수 1과 문자열 "1"은 다름)
올바른 예제:
# 일관된 키 타입 사용
d = {1: "one", 2: "two", 3: "three"}
print(d[1]) # "one"
연습 문제
문제 1: 중복 제거
주어진 리스트에서 중복된 요소를 제거하고 정렬하는 함수를 작성하세요.
문제 2: 학생 성적 관리
학생 이름과 성적을 저장하는 딕셔너리에서 평균 성적을 계산하세요.
문제 3: 빈도 수 계산
문자열에서 각 문자의 빈도를 계산하는 프로그램을 작성하세요.
풀이
문제 1 풀이
def remove_duplicates(lst):
return sorted(list(set(lst)))
numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5]
result = remove_duplicates(numbers)
print(f"원본: {numbers}")
print(f"결과: {result}")
문제 2 풀이
grades = {
"Alice": 85,
"Bob": 92,
"Charlie": 78,
"David": 88
}
average = sum(grades.values()) / len(grades)
print(f"평균 성적: {average:.1f}")
for name, grade in grades.items():
print(f"{name}: {grade}")
문제 3 풀이
def count_characters(text):
char_count = {}
for char in text.lower():
if char != ' ':
char_count[char] = char_count.get(char, 0) + 1
return char_count
result = count_characters("hello world")
print(f"문자 빈도: {result}")
마무리
자료구조를 올바르게 선택하는 것은 프로그램의 성능과 유지보수성에 큰 영향을 미칩니다. 각 자료구조의 특성을 이해하고 상황에 맞게 선택하세요.