5장: 리스트, 튜플, 집합
강의 목표
- 파이썬의 주요 시퀀스 타입인 리스트와 튜플의 개념과 사용법을 이해합니다.
- 리스트의 생성, 인덱싱, 슬라이싱, 수정, 정렬 및 기타 공통 연산을 숙달합니다.
- 튜플의 불변성 및 패킹/언패킹과 리스트와의 차이점을 파악합니다.
- 집합(Set)의 개념, 생성 및 기본 연산을 학습합니다.
- 중첩 리스트와 얕은 복사/깊은 복사의 중요성을 이해합니다.
1. 파이썬 시퀀스 소개
- 파이썬의 주요 시퀀스 타입: 리스트(lists)와 튜플(tuples).
- 리스트는 다른 언어의 배열과 유사하지만, 훨씬 더 유연하고 강력합니다.
- 튜플은 수정할 수 없는 리스트와 같습니다. 제한된 형태의 리스트 또는 기본 레코드 타입으로 생각할 수 있습니다.
- 새로운 컬렉션 타입: 집합(sets)
- 객체의 컬렉션 내 멤버십(포함 여부)이 위치보다 중요할 때 유용합니다.
2. 리스트: 핵심 데이터 구조
2.1 리스트 생성 및 기본
- 정의: 리스트는 객체의 정렬된(ordered) 컬렉션입니다.
- 생성: 쉼표로 구분된 요소들을 대괄호
[]로 묶어 생성합니다.- 예시:
my_list = [1, 'hello', 3.14]
- 예시:
- 동적 크기 조절: 미리 크기를 선언하거나 고정할 필요 없이, 필요에 따라 자동으로 커지거나 작아집니다.
- 다양한 타입의 요소: 파이썬 리스트는 다른 언어의 리스트와 달리 다양한 타입의 요소를 포함할 수 있습니다 (모든 파이썬 객체 가능).
len()함수: 리스트의 요소 개수를 반환합니다.- 주의: 중첩된 리스트 안의 항목은 세지 않습니다.
예제 코드:
# 리스트 생성
x = [1, 2, 3] # 숫자 리스트
print(x) # 출력: [1, 2, 3]
y = [2, "two", [1, 2, 3]] # 혼합 타입
print(y) # 출력: [2, 'two', [1, 2, 3]]
# 길이 확인
print(len(y)) # 출력: 3 (내부 리스트는 세지 않음)2.2 리스트 인덱스 및 슬라이싱
- 인덱싱: C 언어의 배열 인덱싱과 유사하게 0부터 시작합니다.
list은 첫 번째 요소,list은 두 번째 요소 등.
- 음수 인덱싱: 리스트의 끝에서부터 위치를 나타냅니다.
-1은 마지막 요소,-2는 뒤에서 두 번째 요소 등.
- 인덱스의 개념:
- 단일 요소 추출 시에는 인덱스가 특정 요소를 가리킨다고 생각할 수 있습니다.
- 고급 연산(슬라이싱) 시에는 인덱스가 요소 사이의 위치를 나타낸다고 생각하는 것이 더 정확합니다.
- 슬라이싱: 리스트의 하위 리스트(sublist)를 한 번에 추출하거나 할당하는 작업.
list[index1:index2]:index1부터index2직전까지의 모든 항목을 추출하여 새 리스트로 만듭니다.index2가index1보다 앞선 경우 빈 리스트[]를 반환합니다.- 인덱스 생략:
index1생략: 리스트의 처음부터.index2생략: 리스트의 끝까지.- 둘 다 생략:
list[:]는 리스트 전체의 복사본을 만듭니다 (원본에 영향을 주지 않고 수정 가능).
예제 코드:
x = ["first", "second", "third", "fourth"]
# 인덱스 접근
print(x[0]) # 출력: 'first'
print(x[-1]) # 출력: 'fourth'
# 슬라이싱
print(x[1:-1]) # 출력: ['second', 'third']
print(x[:3]) # 출력: ['first', 'second', 'third']
print(x[2:]) # 출력: ['third', 'fourth']
# 두 번째 절반 (Try this)
n = len(x) // 2
print(x[n:]) # 출력: ['third', 'fourth']
# 전체 복사
y = x[:] # 얕은 복사
y[0] = '1st'
print(y) # 출력: ['1st', 'second', 'third', 'fourth']
print(x) # 원본 유지: ['first', 'second', 'third', 'fourth']2.3 리스트 수정
- 인덱스를 이용한 수정: 할당 연산자
=의 왼쪽에 인덱스를 사용하여 요소를 수정할 수 있습니다.- 예시:
my_list = 'new_value'
- 예시:
- 슬라이스 할당:
lista[index1:index2] = listb를 사용하여lista의 해당 슬라이스를listb의 요소들로 교체합니다.listb의 요소 개수에 따라lista의 길이가 변경될 수 있습니다.
- 주요 리스트 메서드:
append(element): 리스트의 끝에 단일 요소를 추가합니다.- 주의: 다른 리스트를
append()하면 중첩 리스트로 추가됩니다.
- 주의: 다른 리스트를
extend(another_list): 한 리스트의 요소들을 다른 리스트의 끝에 확장하여 추가합니다.insert(n, element):n번 인덱스 바로 앞에element를 삽입합니다.- 음수 인덱스도 처리합니다.
del문: 리스트의 항목 또는 슬라이스를 삭제하는 권장 방식.del list[n],del list[m:n].
remove(value): 리스트에서 주어진 값의 첫 번째 인스턴스를 찾아 제거합니다.- 값을 찾지 못하면 오류를 발생시킵니다 (발생 방지를 위해
in연산자 사용).
- 값을 찾지 못하면 오류를 발생시킵니다 (발생 방지를 위해
reverse(): 리스트를 제자리에서(in-place) 역순으로 만듭니다.
예제 코드:
x = [1, 2, 3, 4]
# 인덱스 수정
x[1] = "two"
print(x) # 출력: [1, 'two', 3, 4]
# 슬라이스 할당
x[1:3] = ["new", "data"]
print(x) # 출력: [1, 'new', 'data', 4]
# 메서드
x.append(5)
print(x) # 출력: [1, 'new', 'data', 4, 5]
x.extend([6, 7])
print(x) # 출력: [1, 'new', 'data', 4, 5, 6, 7]
x.insert(0, 0)
print(x) # 출력: [0, 1, 'new', 'data', 4, 5, 6, 7]
del x[1:3]
print(x) # 출력: [0, 'data', 4, 5, 6, 7]
x.remove(4)
print(x) # 출력: [0, 'data', 5, 6, 7]
x.reverse()
print(x) # 출력: [7, 6, 5, 'data', 0]
# Try this: 마지막 3개 요소를 앞으로
x = list(range(10)) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
x = x[-3:] + x[:-3]
print(x) # 출력: [7, 8, 9, 0, 1, 2, 3, 4, 5, 6]2.4 리스트 정렬
list.sort()메서드: 리스트를 제자리에서(in-place) 정렬합니다.- 원본 리스트를 변경합니다.
- 문자열도 정렬 가능합니다.
- 정렬 시 주의사항: 기본 정렬 메서드는 리스트의 모든 항목이 비교 가능한 타입이어야 합니다. 그렇지 않으면 예외가 발생합니다.
sorted()내장 함수: 어떤 이터러블(iterable)이든 새로운 정렬된 리스트를 반환합니다.- 원본을 변경하지 않고 정렬된 복사본을 만들 때 사용합니다.
key및reverse매개변수를sort()메서드와 동일하게 사용합니다.
- 정렬 복사본 만들기:
list[:] .sort()또는sorted(list). - 커스텀 정렬:
key매개변수 사용: 정렬 기준으로 사용할 값을 반환하는 함수를 전달합니다.- 예시: 문자열 길이에 따라 정렬하기 위해
len()함수를key로 사용. - 람다(lambda) 함수: 짧고 한 번만 사용되는 키 함수를 만들 때 유용합니다.
reverse=True: 내림차순 정렬 시 커스텀 정렬 대신 이 매개변수를 사용하는 것이 더 빠릅니다.- 성능 고려: 커스텀 정렬은 기본 정렬보다 느릴 수 있습니다.
예제 코드:
x = [3, 8, 4, 0, 2, 1]
x.sort()
print(x) # 출력: [0, 1, 2, 3, 4, 8]
# 커스텀 정렬
words = ['Python', 'is', 'better', 'than', 'C']
words.sort(key=len)
print(words) # 출력: ['C', 'is', 'than', 'Python', 'better']
# sorted()와 reverse
y = sorted(words, reverse=True)
print(y) # 출력: ['than', 'Python', 'is', 'better', 'C']
# Try this: 두 번째 요소 기준 정렬
x = [[1, 2, 3], [2, 1, 3], [4, 0, 1]]
x.sort(key=lambda x: x[1])
print(x) # 출력: [[4, 0, 1], [2, 1, 3], [1, 2, 3]]2.5 기타 공통 리스트 연산
in연산자: 값이 리스트에 있는지 확인합니다 (Boolean 값 반환).not in도 사용 가능.+연산자 (리스트 연결): 두 리스트를 연결하여 새로운 리스트를 생성합니다 (원본 리스트는 변경되지 않음).*연산자 (리스트 곱셈): 리스트를 지정된 횟수만큼 복제하여 새로운 리스트를 만듭니다.- 미리 크기를 알고 있는 큰 리스트를 초기화할 때 효율적입니다.
min(),max(): 리스트에서 가장 작은/큰 요소를 찾습니다.- 주의: 서로 비교할 수 없는 타입의 요소가 섞여 있으면 오류 발생.
index(value)메서드: 리스트에서 주어진 값의 위치를 반환합니다.- 주의: 값이 리스트에 없으면 오류 발생 (발생 방지를 위해
in연산자 사용).
- 주의: 값이 리스트에 없으면 오류 발생 (발생 방지를 위해
count(value)메서드: 리스트에서 주어진 값이 나타나는 횟수를 반환합니다.Quick check:len([] * 3)의 결과,in연산자와index()메서드의 두 가지 차이점, 예외를 발생시키는 경우.Try this: 리스트x에서 특정 값이 있을 경우에만 안전하게 항목을 제거하는 코드, 해당 항목이 두 번 이상 나타날 경우에만 제거하는 코드로 수정.
예제 코드:
x = [1, 2, 3, 2, 5]
print(3 in x) # 출력: True
print(x + [6, 7]) # 출력: [1, 2, 3, 2, 5, 6, 7]
print([None] * 3) # 출력: [None, None, None]
print(min(x)) # 출력: 1
print(x.index(2)) # 출력: 1
print(x.count(2)) # 출력: 22.6 중첩 리스트와 깊은 복사
- 중첩 리스트: 리스트 안에 다른 리스트를 포함할 수 있습니다. 2차원 행렬 등을 표현하는 데 사용됩니다.
matrix[row][col]와 같이 2차원 인덱스를 사용합니다.
- 변수의 객체 참조: 파이썬 변수는 객체를 참조합니다. 리스트는 변경 가능한(mutable) 객체입니다.
- 얕은 복사(Shallow copy):
x[:],x + [],x * 1등으로 생성합니다.- 최상위 리스트는 새로운 객체지만, 중첩된 리스트는 원본과 동일한 객체를 참조합니다.
- 따라서 얕은 복사본의 중첩된 리스트를 변경하면 원본의 중첩된 리스트도 변경됩니다.
- 얕은 복사(Shallow copy):
- 깊은 복사(Deep copy):
copy모듈의deepcopy()함수를 사용합니다.- 모든 중첩된 객체까지 완전히 독립적인 새 객체로 복사합니다.
- 깊은 복사본을 변경해도 원본 리스트에는 영향을 주지 않습니다.
Try this:x = [,,]리스트가 있을 때,x의 내용을 변경하지 않고y를 수정할 수 있는 복사본을 얻는 코드.
예제 코드:
import copy
x = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
y = x[:] # 얕은 복사
y[0][0] = 0
print(x) # 출력: [[0, 2, 3], [4, 5, 6], [7, 8, 9]] (원본 변경)
z = copy.deepcopy(x)
z[0][0] = 5
print(x) # 출력: [[0, 2, 3], [4, 5, 6], [7, 8, 9]] (원본 유지)3. 튜플
3.1 튜플 기본
- 정의: 리스트와 매우 유사하지만, 수정할 수 없는(immutable) 데이터 구조입니다.
- 생성: 쉼표로 구분된 값들을 괄호
()로 묶어 생성합니다.- 예시:
my_tuple = ('a', 'b', 'c')
- 예시:
- 불변성: 튜플은 생성 후 수정할 수 없습니다. 수정 시
TypeError가 발생합니다. +및*연산자를 사용하여 기존 튜플로부터 새로운 튜플을 만들 수 있습니다.- “복사본”: 튜플은 불변이기 때문에, 리스트처럼
[:]등으로 “복사”해도 실제로는 원본 객체를 가리키는 별칭(alias)을 반환합니다.- 주의: 튜플 안에 리스트나 딕셔너리 같은 변경 가능한 객체가 포함되어 있다면, 해당 객체들은 변경될 수 있습니다. (이런 튜플은 딕셔너리 키로 사용할 수 없습니다).
3.2 한 요소 튜플의 특이점
- 쉼표의 중요성: 괄호
()는 표현식에서 항목을 묶는 데도 사용될 수 있기 때문에, 한 요소 튜플을 만들 때는 요소 뒤에 쉼표를 반드시 붙여야 합니다.- 예시:
my_one_element_tuple = (1,) x = (1)은 튜플이 아니라int타입의x = 1이 됩니다.
- 예시:
- 빈 튜플:
()로 생성하며, 모호성이 없어 쉼표가 필요 없습니다.
3.3 튜플 패킹 및 언패킹
- 패킹(Packing): 여러 값을 하나의 튜플로 묶는 것.
- 언패킹(Unpacking): 튜플의 값을 여러 변수에 할당하는 것.
one, two, three = 1, 2, 3와 같이 괄호를 생략하고 사용 가능.- 변수 간 값 교환 시 매우 편리합니다.
- 확장 언패킹 (Python 3):
*를 사용하여 남은 모든 요소를 리스트로 흡수할 수 있습니다.- 예시:
first, *middle, last = 1, 2, 3, 4, 5
- 예시:
- 리스트도 패킹/언패킹 가능합니다.
예제 코드:
a, b, c = 1, 2, 3
print(a, b, c) # 출력: 1 2 3
a, b = b, a
print(a, b) # 출력: 2 1
x = (1, 2, 3, 4)
a, *b, c = x
print(a, b, c) # 출력: 1 [2, 3] 43.4 리스트와 튜플 간 변환
list(sequence): 어떤 시퀀스(튜플 포함)든 인자로 받아 새로운 리스트를 생성합니다.list("Hello")는['H', 'e', 'l', 'l', 'o']를 반환합니다.
tuple(sequence): 어떤 시퀀스(리스트 포함)든 인자로 받아 새로운 튜플을 생성합니다.
예제 코드:
list((1, 2)) # 출력: [1, 2]
tuple([3, 4]) # 출력: (3, 4)
list("Hello") # 출력: ['H', 'e', 'l', 'l', 'o']
x = (3, 1, 4, 2)
sorted(x) # 출력: [1, 2, 3, 4]4. 집합
4.1 집합의 특성 및 생성
- 정의: 객체의 정렬되지 않은(unordered) 컬렉션이며, 유일한(unique) 요소만을 가집니다.
- 요소의 제약: 딕셔너리 키와 마찬가지로, 집합의 항목은 불변(immutable)이며 해시 가능(hashable)해야 합니다.
int,float,string,tuple은 가능.- 리스트, 딕셔너리, 집합 자체는 불가능.
- 생성:
set(sequence)함수를 사용하여 시퀀스(예: 리스트)로부터 집합을 생성합니다.- 중복된 요소는 제거됩니다.
- 예시:
my_set = set()
예제 코드:
x = set([1, 2, 3, 1, 3, 5])
x # {1, 2, 3, 5}4.2 집합 연산
- 멤버십 확인:
in키워드. - 요소 추가/제거:
add(element),remove(element). - 수학적 집합 연산:
|(Union/합집합): 두 집합의 모든 요소를 포함하는 새 집합.&(Intersection/교집합): 두 집합에 공통으로 있는 요소를 포함하는 새 집합.^(Symmetric Difference/대칭 차집합): 한쪽 집합에는 있지만 양쪽에 모두 있지는 않은 요소를 포함하는 새 집합.
예제 코드:
x = set([1, 2, 3, 5])
x.add(6)
print(x) # 출력: {1, 2, 3, 5, 6}
y = set([4, 5, 6])
print(x | y) # 출력: {1, 2, 3, 4, 5, 6}
print(x & y) # 출력: {5, 6}
print(x ^ y) # 출력: {1, 2, 3, 4}4.3 불변 집합: frozenset
frozenset: 일반set과 달리 생성 후 변경할 수 없는(immutable) 집합 타입.- 활용: 불변이며 해시 가능하기 때문에 다른 집합의 멤버로 포함될 수 있습니다.
- 예시:
my_frozenset = frozenset(my_set)
- 예시:
예제 코드:
z = frozenset([1, 2, 3])
x.add(z)
print(x) # 출력: {1, 2, 3, 5, 6, frozenset({1, 2, 3})}