[2026] Python 자료형 | 리스트, 딕셔너리, 튜플, 세트 완벽 가이드
이 글의 핵심
Python 자료형: 리스트, 딕셔너리, 튜플, 세트 리스트 (List)·딕셔너리 (Dictionary).
들어가며
Python의 내장 자료형(언어에 기본으로 들어 있는 데이터 종류)은 강력하고 사용하기 쉽습니다. 이 글에서는 리스트, 딕셔너리, 튜플, 세트를 완벽하게 마스터합니다. 자료구조 관점(시간 복잡도·알고리즘 문제 풀이)은 배열과 리스트, 스택과 큐, 해시 테이블 글과 함께 보면 이해가 깊어집니다.
실무 활용 사례: 데이터 분석, 웹 개발, 자동화 프로젝트에서 실제로 사용한 패턴과 코드를 바탕으로 정리했습니다. 초보자가 흔히 겪는 오류와 해결법을 포함합니다.
실무에서 느낀 Python의 매력
처음 Python을 배울 때는 “이게 정말 프로그래밍 언어인가?” 싶을 정도로 간결했습니다. C++에서 10줄로 작성하던 코드가 Python에서는 2~3줄로 끝나는 경우가 많았죠. 특히 데이터 분석 프로젝트를 진행하면서 Pandas와 NumPy의 강력함을 체감했습니다. 엑셀로 몇 시간 걸리던 작업이 Python 스크립트로는 몇 초 만에 끝나는 걸 보고 동료들이 놀라워했던 기억이 납니다. 하지만 처음부터 순탄하지만은 않았습니다. 들여쓰기 하나 잘못해서 몇 시간을 헤맨 적도 있고, 가상환경 설정이 꼬여서 프로젝트 전체를 다시 시작한 적도 있습니다. 이런 시행착오를 겪으며 깨달은 건, 환경 설정을 처음부터 제대로 하는 것이 얼마나 중요한지였습니다. 이 글에서는 제가 겪은 실수들을 바탕으로, 여러분이 같은 시행착오를 겪지 않도록 실전 팁을 담았습니다.
1. 리스트 (List)
리스트란?
리스트(List)는 순서가 있고 수정 가능한(mutable) 자료형입니다. 여러 타입의 요소를 담을 수 있습니다. 쇼핑할 때 장바구니에 물건을 순서대로 넣었다가 뺄 수 있는 것과 비슷하게, 끝에 추가(append)하거나 중간을 바꾸는 일이 자유롭습니다.
특징:
- ✅ 순서 유지 (인덱스로 접근)
- ✅ 중복 허용
- ✅ 수정 가능 (추가, 삭제, 변경)
- ✅ 다양한 타입 혼합 가능
리스트 생성과 접근
다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 리스트 생성
fruits = ["apple", "banana", "cherry"]
numbers = [1, 2, 3, 4, 5]
mixed = [1, "hello", 3.14, True, [1, 2]] # 중첩 리스트 가능
# 빈 리스트
empty1 = []
empty2 = list()
# range로 리스트 생성
nums = list(range(10))
print(nums) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 인덱싱 (0부터 시작)
print(fruits[0]) # apple (첫 번째)
print(fruits[1]) # banana (두 번째)
print(fruits[-1]) # cherry (뒤에서 첫 번째)
print(fruits[-2]) # banana (뒤에서 두 번째)
# 인덱스 범위 체크
# print(fruits[10]) # IndexError: list index out of range
# 슬라이싱 [시작:끝:간격]
print(numbers[1:4]) # [2, 3, 4] (인덱스 1~3)
print(numbers[:3]) # [1, 2, 3] (처음부터 인덱스 2까지)
print(numbers[2:]) # [3, 4, 5] (인덱스 2부터 끝까지)
print(numbers[::2]) # [1, 3, 5] (간격 2)
print(numbers[::-1]) # [5, 4, 3, 2, 1] (역순)
# 슬라이싱은 새 리스트 반환 (원본 변경 안 됨)
sub = numbers[1:3]
sub[0] = 100
print(numbers) # [1, 2, 3, 4, 5] (원본 그대로)
print(sub) # [100, 3]
리스트 메서드 상세
다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
fruits = ["apple", "banana"]
# 추가 메서드
fruits.append("cherry") # 끝에 하나 추가
print(fruits) # ['apple', 'banana', 'cherry']
fruits.insert(1, "orange") # 인덱스 1에 삽입 (기존 요소는 뒤로)
print(fruits) # ['apple', 'orange', 'banana', 'cherry']
fruits.extend(["grape", "kiwi"]) # 여러 개 추가 (리스트 병합)
print(fruits) # ['apple', 'orange', 'banana', 'cherry', 'grape', 'kiwi']
# append vs extend 차이
list1 = [1, 2, 3]
list1.append([4, 5]) # 리스트 자체를 요소로 추가
print(list1) # [1, 2, 3, [4, 5]]
list2 = [1, 2, 3]
list2.extend([4, 5]) # 요소들을 개별적으로 추가
print(list2) # [1, 2, 3, 4, 5]
# 삭제 메서드
fruits = ["apple", "banana", "cherry", "banana"]
fruits.remove("banana") # 첫 번째 "banana" 삭제
print(fruits) # ['apple', 'cherry', 'banana']
del fruits[0] # 인덱스로 삭제
print(fruits) # ['cherry', 'banana']
last = fruits.pop() # 마지막 요소 제거 및 반환
print(last) # banana
print(fruits) # ['cherry']
second = fruits.pop(0) # 특정 인덱스 제거 및 반환
print(second) # cherry
fruits.clear() # 전체 삭제
print(fruits) # []
# 검색 메서드
fruits = ["apple", "banana", "cherry", "banana"]
index = fruits.index("banana") # 첫 번째 위치
print(index) # 1
# index("banana", 2) # 인덱스 2부터 검색
# fruits.index("grape") # ValueError: 'grape' is not in list
count = fruits.count("banana") # 개수 세기
print(count) # 2
# in 연산자
print("apple" in fruits) # True
print("grape" in fruits) # False
# 정렬 메서드
numbers = [3, 1, 4, 1, 5, 9, 2]
# sort(): 원본 변경
numbers.sort()
print(numbers) # [1, 1, 2, 3, 4, 5, 9]
numbers.sort(reverse=True) # 내림차순
print(numbers) # [9, 5, 4, 3, 2, 1, 1]
# sorted(): 새 리스트 반환 (원본 유지)
numbers = [3, 1, 4, 1, 5]
sorted_nums = sorted(numbers)
print(numbers) # [3, 1, 4, 1, 5] (원본 그대로)
print(sorted_nums) # [1, 1, 3, 4, 5]
# key 함수로 정렬
words = ["banana", "pie", "Washington", "book"]
words.sort(key=len) # 길이 순
print(words) # ['pie', 'book', 'banana', 'Washington']
words.sort(key=str.lower) # 대소문자 무시
print(words) # ['banana', 'book', 'pie', 'Washington']
# 뒤집기
numbers = [1, 2, 3, 4, 5]
numbers.reverse() # 원본 변경
print(numbers) # [5, 4, 3, 2, 1]
# 또는 슬라이싱
reversed_nums = numbers[::-1] # 새 리스트
리스트 연산
다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 연결 (+)
list1 = [1, 2, 3]
list2 = [4, 5, 6]
combined = list1 + list2
print(combined) # [1, 2, 3, 4, 5, 6]
# 반복 (*)
repeated = [1, 2] * 3
print(repeated) # [1, 2, 1, 2, 1, 2]
# 길이
print(len(fruits)) # 요소 개수
# 최대/최소/합계
numbers = [3, 1, 4, 1, 5]
print(max(numbers)) # 5
print(min(numbers)) # 1
print(sum(numbers)) # 14
리스트 컴프리헨션 (상세)
다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 기본: [표현식 for 변수 in 반복가능객체]
squares = [x**2 for x in range(10)]
print(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# 조건 포함: [표현식 for 변수 in 반복가능객체 if 조건]
evens = [x for x in range(10) if x % 2 == 0]
print(evens) # [0, 2, 4, 6, 8]
# if-else: [표현식1 if 조건 else 표현식2 for 변수 in 반복가능객체]
labels = ['짝수' if x % 2 == 0 else '홀수' for x in range(5)]
print(labels) # ['짝수', '홀수', '짝수', '홀수', '짝수']
# 중첩: 2차원 리스트 생성
matrix = [[i*j for j in range(3)] for i in range(3)]
print(matrix)
# [[0, 0, 0],
# [0, 1, 2],
# [0, 2, 4]]
# 평탄화
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [num for row in matrix for num in row]
print(flat) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
리스트 복사 주의사항
다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 얕은 복사 (shallow copy)
list1 = [1, 2, 3]
list2 = list1 # 참조 복사 (같은 객체)
list2[0] = 100
print(list1) # [100, 2, 3] (list1도 변경됨!)
# 깊은 복사 방법
list1 = [1, 2, 3]
list2 = list1.copy() # 방법 1
list3 = list1[:] # 방법 2
list4 = list(list1) # 방법 3
list2[0] = 100
print(list1) # [1, 2, 3] (원본 유지)
print(list2) # [100, 2, 3]
# 중첩 리스트는 얕은 복사
matrix = [[1, 2], [3, 4]]
matrix_copy = matrix.copy()
matrix_copy[0][0] = 100
print(matrix) # [[100, 2], [3, 4]] (원본도 변경!)
# 완전한 깊은 복사
import copy
matrix_deep = copy.deepcopy(matrix)
matrix_deep[0][0] = 999
print(matrix) # [[100, 2], [3, 4]] (원본 유지)
print(matrix_deep) # [[999, 2], [3, 4]]
2. 딕셔너리 (Dictionary)
기본 사용법
딕셔너리는 이름표(키)로 값을 찾는 전화번호부와 비슷합니다. 리스트처럼 순번(0, 1, 2…)으로 찾기보다 의미 있는 키로 저장하므로, 설정 값·사용자 프로필처럼 “항목 이름 → 내용” 구조에 잘 맞습니다. 다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 딕셔너리 생성
person = {
"name": "홍길동",
"age": 25,
"city": "서울"
}
# 접근
print(person[name]) # 홍길동
print(person.get("age")) # 25
print(person.get("job", "없음")) # 기본값
# 추가/수정
person[job] = "개발자" # 추가
person[age] = 26 # 수정
# 삭제
del person[city]
job = person.pop("job") # 제거 및 반환
딕셔너리 메서드
다음은 python를 활용한 상세한 구현 코드입니다. 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
person = {"name": "홍길동", "age": 25}
# 키/값/아이템
keys = person.keys() # dict_keys(['name', 'age'])
values = person.values() # dict_values(['홍길동', 25])
items = person.items() # dict_items([('name', '홍길동'), ('age', 25)])
# 순회 방법
# 방법 1: 키만
for key in person:
print(key, person[key])
# 방법 2: 키-값 (권장)
for key, value in person.items():
print(f"{key}: {value}")
# name: 홍길동
# age: 25
# update(): 딕셔너리 병합
person.update({"city": "서울", "job": "개발자"}) # age는 덮어씀
print(person) # {'name': '홍길동', 'age': 25, 'city': '서울', 'job': '개발자'}
# setdefault(): 키가 없을 때만 추가
person.setdefault("country", "한국") # 추가
print(person[country]) # 한국
person.setdefault("name", "김철수") # 이미 있으므로 무시
print(person[name]) # 홍길동 (변경 안 됨)
# popitem(): 마지막 키-값 쌍 제거 (Python 3.7+)
last = person.popitem()
print(last) # ('country', '한국')
딕셔너리 컴프리헨션
다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 기본: {키표현식: 값표현식 for 변수 in 반복가능객체}
squares = {x: x**2 for x in range(5)}
print(squares) # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# 조건 포함
even_squares = {x: x**2 for x in range(10) if x % 2 == 0}
print(even_squares) # {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}
# 키-값 교환
original = {"a": 1, "b": 2, "c": 3}
swapped = {v: k for k, v in original.items()}
print(swapped) # {1: 'a', 2: 'b', 3: 'c'}
# 리스트를 딕셔너리로
names = ["Alice", "Bob", "Charlie"]
name_dict = {i: name for i, name in enumerate(names)}
print(name_dict) # {0: 'Alice', 1: 'Bob', 2: 'Charlie'}
중첩 딕셔너리
다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 중첩 딕셔너리
users = {
"user1": {"name": "홍길동", "age": 25},
"user2": {"name": "김철수", "age": 30}
}
# 접근
print(users[user1][name]) # 홍길동
# 안전한 접근
name = users.get("user1", {}).get("name", "없음")
print(name) # 홍길동
# 존재하지 않는 키
name = users.get("user3", {}).get("name", "없음")
print(name) # 없음 (에러 없음)
defaultdict 활용
다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from collections import defaultdict
# 기본 딕셔너리 문제
word_count = {}
words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
for word in words:
if word not in word_count:
word_count[word] = 0
word_count[word] += 1
print(word_count) # {'apple': 3, 'banana': 2, 'cherry': 1}
# defaultdict 사용
word_count = defaultdict(int) # 기본값 0
for word in words:
word_count[word] += 1 # 키가 없어도 자동으로 0으로 초기화
print(dict(word_count)) # {'apple': 3, 'banana': 2, 'cherry': 1}
# 리스트를 기본값으로
groups = defaultdict(list)
students = [("홍길동", "A"), ("김철수", "B"), ("이영희", "A")]
for name, grade in students:
groups[grade].append(name)
print(dict(groups)) # {'A': ['홍길동', '이영희'], 'B': ['김철수']}
3. 튜플 (Tuple)
튜플이란?
튜플(Tuple)은 순서가 있고 수정 불가능한(immutable) 자료형입니다. 리스트와 비슷하지만 한 번 생성하면 변경할 수 없습니다. 특징:
- ✅ 순서 유지
- ✅ 중복 허용
- ❌ 수정 불가 (추가, 삭제, 변경 불가)
- ✅ 리스트보다 메모리 효율적
- ✅ 딕셔너리 키로 사용 가능 언제 사용하나?
- 변경되면 안 되는 데이터 (좌표, 설정값)
- 함수에서 여러 값 반환
- 딕셔너리 키로 사용
튜플 생성과 접근
다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 튜플 생성
point = (10, 20)
person = ("홍길동", 25, "서울")
single = (42,) # 요소 1개일 때 쉼표 필수!
# 괄호 없이도 가능
coords = 10, 20, 30
print(type(coords)) # <class 'tuple'>
# 빈 튜플
empty1 = ()
empty2 = tuple()
# 리스트를 튜플로
numbers = tuple([1, 2, 3, 4, 5])
print(numbers) # (1, 2, 3, 4, 5)
# 인덱싱/슬라이싱 (리스트와 동일)
print(person[0]) # 홍길동
print(person[-1]) # 서울
print(person[1:]) # (25, '서울')
# ❌ 수정 불가
# person[1] = 26 # TypeError: 'tuple' object does not support item assignment
# person.append(100) # AttributeError: 'tuple' object has no attribute 'append'
# 튜플 메서드 (2개만 존재)
numbers = (1, 2, 3, 2, 4, 2)
print(numbers.count(2)) # 3 (2의 개수)
print(numbers.index(3)) # 2 (3의 인덱스)
튜플 언패킹 (Unpacking)
다음은 python를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 기본 언패킹
name, age, city = ("홍길동", 25, "서울")
print(name, age, city) # 홍길동 25 서울
# 함수 반환값
def get_user():
return "홍길동", 25, "서울" # 튜플 반환
name, age, city = get_user()
print(name) # 홍길동
# 값 교환 (Python의 강력한 기능)
a, b = 1, 2
a, b = b, a # 교환
print(a, b) # 2 1
# * 연산자로 나머지 받기
numbers = (1, 2, 3, 4, 5)
first, *rest, last = numbers
print(first) # 1
print(rest) # [2, 3, 4] (리스트!)
print(last) # 5
# 여러 변수에 동시 할당
x, y, z = 10, 20, 30
print(x, y, z) # 10 20 30
튜플 vs 리스트 비교
| 특징 | 튜플 | 리스트 |
|---|---|---|
| 수정 가능 | ❌ | ✅ |
| 속도 | 빠름 | 느림 |
| 메모리 | 적음 | 많음 |
| 메서드 | 2개 | 11개 |
| 딕셔너리 키 | ✅ | ❌ |
| 사용 시점 | 불변 데이터 | 가변 데이터 |
| 아래 코드는 python를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다. |
import sys
# 메모리 비교
list_data = [1, 2, 3, 4, 5]
tuple_data = (1, 2, 3, 4, 5)
print(sys.getsizeof(list_data)) # 104 bytes
print(sys.getsizeof(tuple_data)) # 80 bytes
# 딕셔너리 키로 사용
# locations = {[10, 20]: "A"} # TypeError: unhashable type: 'list'
locations = {(10, 20): "A", (30, 40): "B"} # 튜플은 가능
print(locations[(10, 20)]) # A
튜플의 불변성 주의사항
아래 코드는 python를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 튜플 자체는 불변이지만, 내부 가변 객체는 변경 가능
tuple_with_list = (1, 2, [3, 4])
# tuple_with_list[0] = 100 # TypeError (튜플 요소 변경 불가)
tuple_with_list[2].append(5) # 내부 리스트는 변경 가능
print(tuple_with_list) # (1, 2, [3, 4, 5])
# 튜플 "수정" (실제로는 새 튜플 생성)
original = (1, 2, 3)
modified = original + (4, 5) # 새 튜플
print(original) # (1, 2, 3)
print(modified) # (1, 2, 3, 4, 5)
4. 세트 (Set)
세트란?
세트(Set)는 순서가 없고 중복을 허용하지 않는 자료형입니다. 수학의 집합과 동일합니다. 특징:
- ❌ 순서 없음 (인덱스 접근 불가)
- ❌ 중복 불가 (자동 제거)
- ✅ 수정 가능 (추가, 삭제)
- ✅ 멤버십 테스트 빠름 (O(1))
- ✅ 집합 연산 지원 언제 사용하나?
- 중복 제거
- 멤버십 테스트 (
in연산) - 집합 연산 (합집합, 교집합, 차집합)
세트 생성과 연산
다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 세트 생성
fruits = {"apple", "banana", "cherry"}
numbers = {1, 2, 3, 4, 5}
# 빈 세트 (주의: {}는 딕셔너리!)
empty = set() # 올바른 방법
# empty = {} # 이건 딕셔너리!
# 리스트를 세트로 (중복 제거)
numbers = [1, 2, 2, 3, 3, 3, 4]
unique = set(numbers)
print(unique) # {1, 2, 3, 4}
# 문자열을 세트로
chars = set("hello")
print(chars) # {'h', 'e', 'l', 'o'} (중복 'l' 제거)
# 추가
fruits = {"apple", "banana"}
fruits.add("cherry")
print(fruits) # {'apple', 'banana', 'cherry'}
fruits.add("apple") # 중복은 무시됨
print(fruits) # {'apple', 'banana', 'cherry'}
# 여러 개 추가
fruits.update(["orange", "grape"])
print(fruits) # {'apple', 'banana', 'cherry', 'orange', 'grape'}
# 삭제
fruits.remove("banana") # 없으면 KeyError
print(fruits) # {'apple', 'cherry', 'orange', 'grape'}
fruits.discard("grape") # 없어도 에러 안 남
fruits.discard("kiwi") # 에러 없음
# pop(): 임의의 요소 제거 (순서 없으므로 어떤 요소인지 모름)
item = fruits.pop()
print(item) # 임의의 과일
fruits.clear() # 전체 삭제
집합 연산 상세
다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
# 합집합 (Union): 두 집합의 모든 요소
print(a | b) # {1, 2, 3, 4, 5, 6}
print(a.union(b)) # {1, 2, 3, 4, 5, 6}
# 교집합 (Intersection): 공통 요소
print(a & b) # {3, 4}
print(a.intersection(b)) # {3, 4}
# 차집합 (Difference): a에만 있는 요소
print(a - b) # {1, 2}
print(a.difference(b)) # {1, 2}
# 대칭 차집합 (Symmetric Difference): 한쪽에만 있는 요소
print(a ^ b) # {1, 2, 5, 6}
print(a.symmetric_difference(b)) # {1, 2, 5, 6}
# 부분집합/상위집합 확인
c = {1, 2}
print(c.issubset(a)) # True (c는 a의 부분집합)
print(a.issuperset(c)) # True (a는 c의 상위집합)
# 서로소 집합 확인
d = {7, 8, 9}
print(a.isdisjoint(d)) # True (공통 요소 없음)
print(a.isdisjoint(b)) # False (3, 4가 공통)
세트 실전 활용
다음은 python를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 1. 중복 제거 (순서 유지 필요 없을 때)
emails = ["a@test.com", "b@test.com", "a@test.com", "c@test.com"]
unique_emails = list(set(emails))
print(unique_emails) # ['a@test.com', 'b@test.com', 'c@test.com']
# 2. 중복 제거 (순서 유지 필요할 때)
def remove_duplicates(items):
seen = set()
result = []
for item in items:
if item not in seen:
seen.add(item)
result.append(item)
return result
emails = ["a@test.com", "b@test.com", "a@test.com", "c@test.com"]
unique = remove_duplicates(emails)
print(unique) # ['a@test.com', 'b@test.com', 'c@test.com']
# 3. 빠른 멤버십 테스트
# 리스트: O(n)
large_list = list(range(100000))
# 10000 in large_list # 느림
# 세트: O(1)
large_set = set(range(100000))
# 10000 in large_set # 빠름
# 4. 두 리스트의 공통 요소
list1 = [1, 2, 3, 4, 5]
list2 = [4, 5, 6, 7, 8]
common = list(set(list1) & set(list2))
print(common) # [4, 5]
실전 예제
예제 1: 학생 성적 관리 시스템
# 학생 데이터 (리스트 + 딕셔너리)
students = [
{"name": "홍길동", "scores": [85, 90, 78]},
{"name": "김철수", "scores": [92, 88, 95]},
{"name": "이영희", "scores": [78, 82, 80]}
]
# 평균 계산 및 등급 부여
for student in students:
avg = sum(student[scores]) / len(student[scores])
if avg >= 90:
grade = "A"
elif avg >= 80:
grade = "B"
else:
grade = "C"
print(f"{student['name']}: 평균 {avg:.1f}, 등급 {grade}")
# 출력:
# 홍길동: 평균 84.3, 등급 B
# 김철수: 평균 91.7, 등급 A
# 이영희: 평균 80.0, 등급 B
예제 2: 텍스트 분석 (딕셔너리 + 세트)
text = """
Python is powerful. Python is easy.
Python is popular. Python is versatile.
"""
# 단어 추출 (소문자 변환, 구두점 제거)
words = text.lower().replace(".", "").split()
# 단어 빈도 (딕셔너리)
word_count = {}
for word in words:
word_count[word] = word_count.get(word, 0) + 1
# 빈도순 정렬
sorted_words = sorted(word_count.items(), key=lambda x: x[1], reverse=True)
print("단어 빈도:")
for word, count in sorted_words[:5]: # 상위 5개
print(f" {word}: {count}")
# 고유 단어 개수 (세트)
unique_words = set(words)
print(f"\n고유 단어 수: {len(unique_words)}")
# 출력:
# 단어 빈도:
# python: 4
# is: 4
# powerful: 1
# easy: 1
# popular: 1
#
# 고유 단어 수: 6
예제 3: 장바구니 시스템 (딕셔너리 + 리스트)
다음은 python를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 상품 정보
products = {
"P001": {"name": "노트북", "price": 1200000},
"P002": {"name": "마우스", "price": 30000},
"P003": {"name": "키보드", "price": 80000}
}
# 장바구니 (상품ID: 수량)
cart = {}
# 상품 추가
def add_to_cart(product_id, quantity=1):
if product_id in products:
cart[product_id] = cart.get(product_id, 0) + quantity
print(f"{products[product_id]['name']} {quantity}개 추가")
else:
print("존재하지 않는 상품입니다.")
# 총액 계산
def calculate_total():
total = 0
for product_id, quantity in cart.items():
price = products[product_id][price]
total += price * quantity
return total
# 실행
add_to_cart("P001", 1) # 노트북 1개 추가
add_to_cart("P002", 2) # 마우스 2개 추가
add_to_cart("P003", 1) # 키보드 1개 추가
print(f"\n총액: {calculate_total():,}원") # 총액: 1,340,000원
예제 4: 친구 추천 시스템 (세트)
다음은 python를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 사용자별 친구 목록
friends = {
"홍길동": {"김철수", "이영희", "박민수"},
"김철수": {"홍길동", "박민수", "최지훈"},
"이영희": {"홍길동", "최지훈", "정수진"}
}
# 공통 친구 찾기
def find_common_friends(user1, user2):
return friends[user1] & friends[user2]
# 친구 추천 (친구의 친구 - 나와 내 친구 제외)
def recommend_friends(user):
my_friends = friends[user]
candidates = set()
for friend in my_friends:
candidates.update(friends[friend])
# 나 자신과 이미 친구인 사람 제외
recommendations = candidates - my_friends - {user}
return recommendations
print("홍길동과 김철수의 공통 친구:", find_common_friends("홍길동", "김철수"))
# 홍길동과 김철수의 공통 친구: {'박민수'}
print("홍길동에게 추천할 친구:", recommend_friends("홍길동"))
# 홍길동에게 추천할 친구: {'최지훈', '정수진'}
예제 5: 데이터 변환 파이프라인
# CSV 데이터 (문자열)
csv_data = """name,age,city
홍길동,25,서울
김철수,30,부산
이영희,28,서울
박민수,35,대구"""
# 1단계: 문자열 → 리스트
lines = csv_data.strip().split("\n")
header = lines[0].split(",")
rows = [line.split(",") for line in lines[1:]]
# 2단계: 리스트 → 딕셔너리 리스트
users = []
for row in rows:
user = {}
for i, key in enumerate(header):
user[key] = row[i]
users.append(user)
print("딕셔너리 리스트:")
for user in users:
print(user)
# 3단계: 도시별 그룹핑 (딕셔너리 + 리스트)
city_groups = {}
for user in users:
city = user[city]
if city not in city_groups:
city_groups[city] = []
city_groups[city].append(user[name])
print("\n도시별 사용자:")
for city, names in city_groups.items():
print(f"{city}: {', '.join(names)}")
# 4단계: 고유 도시 (세트)
cities = {user[city] for user in users}
print(f"\n고유 도시: {cities}")
# 출력:
# 딕셔너리 리스트:
# {'name': '홍길동', 'age': '25', 'city': '서울'}
# {'name': '김철수', 'age': '30', 'city': '부산'}
# {'name': '이영희', 'age': '28', 'city': '서울'}
# {'name': '박민수', 'age': '35', 'city': '대구'}
#
# 도시별 사용자:
# 서울: 홍길동, 이영희
# 부산: 김철수
# 대구: 박민수
#
# 고유 도시: {'서울', '부산', '대구'}
성능 비교와 선택 가이드
시간 복잡도 비교
| 연산 | 리스트 | 튜플 | 딕셔너리 | 세트 |
|---|---|---|---|---|
| 인덱스 접근 | O(1) | O(1) | - | - |
| 키/값 접근 | - | - | O(1) | - |
| 검색 (in) | O(n) | O(n) | O(1) | O(1) |
| 추가 (끝) | O(1) | - | O(1) | O(1) |
| 삽입 (중간) | O(n) | - | - | - |
| 삭제 | O(n) | - | O(1) | O(1) |
| 정렬 | O(n log n) | - | - | - |
자료형 선택 가이드
다음은 python를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 1. 순서가 중요하고 수정이 필요한 경우 → 리스트
todo_list = ["코딩", "운동", "독서"]
todo_list.append("명상")
# 2. 순서가 중요하지만 수정이 불필요한 경우 → 튜플
coordinates = (37.5665, 126.9780) # 서울 좌표 (변경 불가)
# 3. 키로 빠르게 접근해야 하는 경우 → 딕셔너리
user_db = {"user123": {"name": "홍길동", "email": "hong@test.com"}}
user = user_db[user123] # O(1) 접근
# 4. 중복 제거나 집합 연산이 필요한 경우 → 세트
tags1 = {"python", "coding", "tutorial"}
tags2 = {"python", "web", "tutorial"}
common_tags = tags1 & tags2 # {'python', 'tutorial'}
# 5. 빠른 멤버십 테스트가 필요한 경우 → 세트
allowed_users = {"admin", "user1", "user2"} # 세트 (빠름)
if username in allowed_users: # O(1)
grant_access()
자주 하는 실수와 해결법
실수 1: 빈 세트를 {}로 생성
아래 코드는 python를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# ❌ 잘못된 방법
empty = {}
print(type(empty)) # <class 'dict'> (딕셔너리!)
# ✅ 올바른 방법
empty = set()
print(type(empty)) # <class 'set'>
실수 2: 리스트 복사 시 참조 문제
아래 코드는 python를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# ❌ 잘못된 방법
list1 = [1, 2, 3]
list2 = list1 # 참조만 복사
list2[0] = 100
print(list1) # [100, 2, 3] (원본도 변경!)
# ✅ 올바른 방법
list1 = [1, 2, 3]
list2 = list1.copy() # 또는 list1[:] 또는 list(list1)
list2[0] = 100
print(list1) # [1, 2, 3] (원본 유지)
실수 3: 딕셔너리 키 에러
아래 코드는 python를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# ❌ 잘못된 방법
person = {"name": "홍길동"}
# print(person[age]) # KeyError: 'age'
# ✅ 올바른 방법 1: get() 사용
age = person.get("age", 0) # 기본값 0
print(age) # 0
# ✅ 올바른 방법 2: in 체크
if "age" in person:
print(person[age])
else:
print("나이 정보 없음")
실수 4: 튜플 요소 1개 생성
아래 코드는 python를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# ❌ 잘못된 방법
single = (42)
print(type(single)) # <class 'int'> (정수!)
# ✅ 올바른 방법
single = (42,) # 쉼표 필수
print(type(single)) # <class 'tuple'>
실수 5: 세트는 순서가 없음
아래 코드는 python를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# ❌ 잘못된 방법
numbers = {3, 1, 4, 1, 5}
# print(numbers[0]) # TypeError: 'set' object is not subscriptable
# ✅ 올바른 방법: 리스트로 변환 후 접근
numbers_list = list(numbers)
print(numbers_list[0]) # 1 (정렬되지 않은 순서)
# 정렬이 필요하면
sorted_numbers = sorted(numbers)
print(sorted_numbers) # [1, 3, 4, 5]
고급 활용 패턴
패턴 1: 리스트 필터링과 변환
아래 코드는 python를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 데이터
users = [
{"name": "홍길동", "age": 25, "active": True},
{"name": "김철수", "age": 17, "active": False},
{"name": "이영희", "age": 30, "active": True}
]
# 활성 성인 사용자만 추출
active_adults = [
user[name]
for user in users
if user[age] >= 18 and user[active]
]
print(active_adults) # ['홍길동', '이영희']
패턴 2: 딕셔너리 병합 (Python 3.9+)
아래 코드는 python를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 방법 1: | 연산자 (Python 3.9+)
dict1 = {"a": 1, "b": 2}
dict2 = {"b": 3, "c": 4}
merged = dict1 | dict2 # dict2가 우선
print(merged) # {'a': 1, 'b': 3, 'c': 4}
# 방법 2: update()
dict1 = {"a": 1, "b": 2}
dict1.update(dict2)
print(dict1) # {'a': 1, 'b': 3, 'c': 4}
# 방법 3: ** 언패킹
merged = {**dict1, **dict2}
print(merged) # {'a': 1, 'b': 3, 'c': 4}
패턴 3: 리스트를 딕셔너리로 그룹핑
from collections import defaultdict
# 데이터
transactions = [
("2026-03-01", "식비", 15000),
("2026-03-01", "교통비", 5000),
("2026-03-02", "식비", 20000),
("2026-03-02", "쇼핑", 50000),
("2026-03-03", "식비", 12000)
]
# 날짜별 그룹핑
by_date = defaultdict(list)
for date, category, amount in transactions:
by_date[date].append((category, amount))
print("날짜별 지출:")
for date, items in by_date.items():
total = sum(amount for _, amount in items)
print(f"{date}: {total:,}원")
# 카테고리별 합계
by_category = defaultdict(int)
for _, category, amount in transactions:
by_category[category] += amount
print("\n카테고리별 지출:")
for category, total in by_category.items():
print(f"{category}: {total:,}원")
# 출력:
# 날짜별 지출:
# 2026-03-01: 20,000원
# 2026-03-02: 70,000원
# 2026-03-03: 12,000원
#
# 카테고리별 지출:
# 식비: 47,000원
# 교통비: 5,000원
# 쇼핑: 50,000원
패턴 4: 세트로 권한 관리
다음은 python를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 역할별 권한
permissions = {
"admin": {"read", "write", "delete", "manage_users"},
"editor": {"read", "write"},
"viewer": {"read"}
}
# 사용자 역할
user_roles = {
"홍길동": [admin],
"김철수": ["editor", "viewer"],
"이영희": [viewer]
}
# 사용자의 모든 권한 계산
def get_user_permissions(username):
user_perms = set()
for role in user_roles.get(username, []):
user_perms.update(permissions.get(role, set()))
return user_perms
# 권한 확인
def has_permission(username, permission):
return permission in get_user_permissions(username)
# 테스트
print("홍길동 권한:", get_user_permissions("홍길동"))
# {'read', 'write', 'delete', 'manage_users'}
print("김철수 권한:", get_user_permissions("김철수"))
# {'read', 'write'}
print("이영희가 write 가능?", has_permission("이영희", "write"))
# False
연습 문제
문제 1: 리스트 회전
리스트를 오른쪽으로 k번 회전하세요. 아래 코드는 python를 사용한 구현 예제입니다. 함수를 통해 로직을 구현합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
def rotate_list(nums, k):
if not nums or k == 0:
return nums
k = k % len(nums) # k가 리스트 길이보다 클 경우
return nums[-k:] + nums[:-k]
# 테스트
print(rotate_list([1, 2, 3, 4, 5], 2)) # [4, 5, 1, 2, 3]
print(rotate_list([1, 2, 3, 4, 5], 7)) # [4, 5, 1, 2, 3] (7 % 5 = 2)
문제 2: 두 딕셔너리의 차이 찾기
두 딕셔너리를 비교하여 다른 키-값 쌍을 찾으세요. 다음은 python를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
def dict_diff(dict1, dict2):
all_keys = set(dict1.keys()) | set(dict2.keys())
diff = {}
for key in all_keys:
val1 = dict1.get(key)
val2 = dict2.get(key)
if val1 != val2:
diff[key] = {"old": val1, "new": val2}
return diff
# 테스트
old = {"name": "홍길동", "age": 25, "city": "서울"}
new = {"name": "홍길동", "age": 26, "city": "부산", "job": "개발자"}
print(dict_diff(old, new))
# {'age': {'old': 25, 'new': 26},
# 'city': {'old': '서울', 'new': '부산'},
# 'job': {'old': None, 'new': '개발자'}}
문제 3: 중첩 리스트 평탄화
중첩된 리스트를 1차원 리스트로 변환하세요. 다음은 python를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
def flatten(nested_list):
result = []
for item in nested_list:
if isinstance(item, list):
result.extend(flatten(item)) # 재귀
else:
result.append(item)
return result
# 테스트
nested = [1, [2, 3], [4, [5, 6]], 7]
print(flatten(nested)) # [1, 2, 3, 4, 5, 6, 7]
# 컴프리헨션 버전 (1단계만)
nested = [[1, 2], [3, 4], [5, 6]]
flat = [num for sublist in nested for num in sublist]
print(flat) # [1, 2, 3, 4, 5, 6]
정리
자료형 선택 가이드
| 자료형 | 순서 | 중복 | 수정 | 용도 |
|---|---|---|---|---|
| 리스트 | ✅ | ✅ | ✅ | 순서 있는 데이터, 수정 필요 |
| 튜플 | ✅ | ✅ | ❌ | 불변 데이터, 함수 반환값 |
| 딕셔너리 | ✅ (3.7+) | ❌ (키) | ✅ | 키-값 쌍, 빠른 조회 |
| 세트 | ❌ | ❌ | ✅ | 중복 제거, 집합 연산, 빠른 멤버십 |
핵심 요약
- 리스트: 가장 범용적,
append(),extend(),sort()활용 - 딕셔너리: 키로 빠른 접근,
get(),items(),defaultdict활용 - 튜플: 불변 데이터, 언패킹, 딕셔너리 키로 사용
- 세트: 중복 제거,
in연산 빠름, 집합 연산 지원
다음 단계
- Python 함수
- Python 컴프리헨션
- Python 클래스