일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- database
- 코딩테스트
- Binary Tree
- Math
- 구현
- sorting
- 파이썬
- 코테
- two pointers
- hash table
- geometry
- greedy
- Stack
- Counting
- Data Structure
- Tree
- 자바
- Binary Search
- implement
- Number Theory
- Matrix
- Class
- bit manipulation
- Method
- SQL
- java
- dynamic programming
- array
- string
- simulation
- Today
- Total
코린이의 소소한 공부노트
데이터 타입, 연산자 (4) 딕셔너리, 셋 본문
컬렉션 타입은 앞서 본 리스트와 튜플 외에 2가지가 더 있다.
이번에 살펴볼 것은
1. 사전과
2. 집합이다.
여기까지 다 봤다면 파이썬에 대해서 반이나 알게 된 것이다! (시작이 반이다!!!)
1. 사전(dictionary, 줄여서 딕트[dict]라고 씀)
보통 사전이라함은.. ㄱㄴㄷ, abc 등 문자의 순서대로 단어들을 쫙 나열해놓고 옆에 단어의 뜻, 예시 등이 적힌 책을 말하는데, 파이썬에서 말하는 사전은 약간 다르다.
파이썬의 사전(dict)은 키(key)와 값(value)으로 구성되어 있다. 사전의 단어가 dict의 키, 사전의 단어의 뜻이 dict의 값에 해당한다고 보면 되겠다. 물론 키와 값은 내 맘대로 집어넣을 수 있다.
dict1 = {'아침':'토스트', '점심':'쌀국수', '저녁':'곱창', 1:'하나'}
dict2 = {} # 빈 dict
type(dict2)
>> dict
dict1[???] # 무엇을 입력해야
>> 출-력 # 어떤 것이 출력되는 걸까?
dict2처럼 중괄호 {}만 써서 아무것도 없는 dict를 만들어서 나중에 값을 추가해도 되고, dict1처럼 처음부터 넣고 싶은 키와 값을 :으로 묶어서 써도 된다. 키와 값은 타입이 같지 않아도 상관없다. 그리고 리스트처럼 인덱스 대신 키를 넣어서 원하는 값을 출력할 수도 있다. 여기서 중요한 것이 2가지가 있다. 위의 ???에 들어가는 것은 리스트와는 다르다는 것, 키를 입력했을 때 내부에서 키를 기준으로 오름차순 또는 내림차순으로 정렬하지 않는다는 것이 그것이다.
dict3 = {2:'b', 6:2, 0:'a'}
dict3[2]
>> ??
dict3을 선언할 때 키의 값들이 2, 6, 0이니까 내부적으로 {0:'a', 2:'b', 6:2}로 정리될 것 같지만 그렇지 않다. dict에서 키의 값은 주소(hash)에 불과하다. 그래서 딱히 정렬하지 않는 것이다. 두 번째 줄에 있는 dict3[2]를 입력해서 결과를 출력해보면 어떤 값이 나올까?
ㄱ. 2를 인덱스로 인식해서 0:'a'의 'a'를 출력한다
ㄴ. 값이 2인 6:2를 쫓아가서 6을 출력한다
ㄷ. 2가 주소니까 키의 값이 2인 2:'b'를 쫓아가서 'b'를 출력한다.
정답은 ㄷ이다. 우리가 dict를 선언할 때 넣은 순서가 인덱스가 되지 않냐는 생각을 할 수 있다. 하지만 앞서 말했듯이 정렬을 해서 0번째, 1번째가 있는 것이 아니다. 그래서 인덱스가 있을 수가 없기 때문에, 주소인 키를 알려줘야 값을 출력할 수 있다. 이 2가지 특징만 빼면 나머지는 리스트와 아주 흡사하다.
첫 번째, 항목을 추가하고 변경할 수 있다.
a = {1:'하나', 2:'둘', 3:'셋'}
a[4] = '넷' # 새로운 키 등장 -> 값 추가
print(a)
>> {1: '하나', 2: '둘', 3: '셋', 4: '넷'}
a[2] = '이' # 있던 키 -> 값 변경, 즉 키는 중복 불가
print(a)
>> {1: '하나', 2: '이', 3: '셋', 4: '넷'}
a[5] = '넷' # 값은 중복 가능
print(a)
>> {1: '하나', 2: '이', 3: '셋', 4: '넷', 5: '넷'}
두 번째, update 함수를 이용해 dict를 합칠 수도 있다.
a = {1:'하나', 2:'둘', 3:'셋'}
b = {4:'넷', 5:'다섯', 3:'삼'}
a.update(b) # b에 있는 키와 값을 a에 덮어쓰기(overwrite)
print(a) # a에 없던 키와 값 추가 + 키 3이 겹쳐서 b의 값으로 바뀜
>> {1: '하나', 2: '둘', 3: '삼', 4: '넷', 5: '다섯'}
세 번째, pop 함수를 이용해서 키를 삭제할 수도 있다.
a = {1:'하나', 2:'둘', 3:'셋'}
b = a.pop(1) # 키를 입력하면 해당하는 값을 반환해준 후 dict에서 삭제
print(b) # 현재 b = '하나'
>> 하나
print(a) # 1:'하나'가 제거된 상태
>> {2: '둘', 3: '셋'}
네 번째, clear 함수를 이용해서 dict를 초기화시킬 수도 있다.
a = {1:'하나', 2:'둘', 3:'셋'}
a.clear() # 싹 다 지워버리기
print(a)
>> {}
다섯 번째, in 키워드를 이용해서 키가 존재하는지 확인할 수 있다.
a = {1:'하나', 2:'둘', 3:'셋'}
4 in a
>> False
1 in a
>> True
in의 경우 리스트나 튜플도 사용 가능한데, 연산 속도에서 차이가 난다.
# 100개씩 들어있는 dict와 리스트
a = {1:'일', 2:'이', ...중략..., 100:'백'}
b = [1, 2, ... 중략..., 100]
100 in a # 100을 주소로 하는 것을 한 번에 찾아감
>> True
100 in b # 100을 찾기 위해 맨 앞부터 찾아봄
>> True
dict의 경우 키를 주소로 바로 찾아가기 때문에 dict의 크기와는 관계없이 연산속도가 일정하지만, 리스트의 경우 리스트의 길이와 연산속도가 비례하게 되므로 엄~청 긴 리스트에서 in을 수행할 경우 속도가 엄~청 느릴 수 있다. (dict의 in 연산처럼 크기와 관계없이 연산 속도가 일정한 것을 O1(오원) 연산이라고 한다.)
dict는 리스트나 튜플과는 다르게 키와 값이 쌍을 이루고 있기 때문에, 불러올 값을 따로 지정할 수 있게 해 놨다.
a = {1:'일', 2:'이', 3:'삼'}
print(a.keys()) # 키만 반환
>> dict_keys([1, 2, 3])
print(a.values()) # 값만 반환
>> dict_values(['일', '이', '삼'])
print(a.items()) # 키와 값을 튜플로 반환
>> dict_items([(1, '일'), (2, '이'), (3, '삼')])
list(a.items()) # 윗줄의 items를 리스트로 변환
>> [(1, '일'), (2, '이'), (3, '삼')]
위에서 보이는 dict_keys/values/items는 클래스(타입)를 나타내는 것이다. 언젠가 코딩을 할 때 유용하게 쓰일 것이다.
하지만 우리는 아직 dict[key]로 값을 꺼내오는 게 아직은 더 익숙하다. 다시 한번 값을 꺼내보자.
a = {1:'일', 2:'이', 3:'삼'}
print(a[1])
>> 일
# .. 중간에 여러가지 코드들이 동작을 하다가..
print(a[4]) # 4라는 키는 a dict에 없으니까
>> KeyError: 4 # 여기서 프로그램은 멈추게 됨
# 이 이후에 있는 코드는 실행되지 않음
열심히 코드를 작성하고 한꺼번에 실행하고 있는 와중에 dict에 키가 없다는 이유로 프로그램이 멈추면 너무 화가 나지 않을까?(아닌가..?) 그럴 땐 키가 없어도 안 멈추게 코드를 작성하면 되겠다!!
a = {1:'일', 2:'이', 3:'삼'}
print(a[1])
>> 일
# .. 중간에 여러가지 코드들이 동작을 하다가..
print(a.get(4)) # 4라는 키는 a dict에 없으니까
>> None # 없다고 출력해주고
# 이 이후에 있는 코드를 실행함
dict[key]로 dict에 접근했을 때 키가 없으면 에러 메시지를 띄우고 프로그램이 종료되지만, get(key) 함수를 이용해서 dict에 접근했을 때 키가 없으면 None을 반환하고 프로그램은 종료되지 않는다. 이것도 꽤 유용하게 쓰일 것 같다!
2. 집합(set)
집합은 우리가 수학 시간에 배운 그 집합이다. 원소들의 중복을 허용하지 않고, 원소 간의 순서가 정해져 있지 않다. (그래서 수학에서 집합을 나눠서 문제를 풀 때 순서를 따지지 않는 조합으로 문제를 푼다.)
a = {1, 1, 3, 4, 2, 2, 2}
print(a)
>> {1, 3, 4, 2}
집합은 dict처럼 중괄호 {}로 둘러싸서 정의하게 된다. 그렇다면 빈 집합은 어떻게 표현하지..?
a = {}
print(type(a)) # 앞서 이것은 dict라고 설명했는데..
>> <class 'dict'>
b = set()
print(type(b)) # 아하 set()이 빈 집합을 만드는 함수구나!
>> <class 'set'>
c = [1, 2, 3]
print(set(c)) # set에 뭔가를 넣으면 집합으로 바꿔주는구나!
>> {1, 2, 3}
set 함수는 집합을 만들어준다. 빈 집합을 dict와 같이 쓰는 경우 빈 집합 선언을 중괄호 {}만 가지고 하지 않도록 조심하도록 하자.
집합 연산은 우리가 수학 시간에 익히 배운 그 연산들을 이용한다.
a = {1,2,3}
b = {2,3,4}
print(a.union(b)) # 합집합
>> {1, 2, 3, 4}
print(a.intersection(b)) # 교집합
>> {2, 3}
print(a.difference(b)) # 차집합
>> {1}
print(a.issubset(b)) # a는 b의 부분집합인가?
>> False
a.add(5) # a에 원소 5 추가
print(a)
>> {1, 2, 3, 5}
a.remove(5) # a에 원소 5 제거
print(a)
>> {1, 2, 3}
이 외에도 많은 집합 연산이 있다. 요정도만 알아도 큰 문제는 없을 듯하다!
<요약>
1. 사전(dict): key : value 쌍, 순서X
- 키는 인덱스X, 주소O > O1 연산
- update, clear, pop 등 사용 가능
2. 집합(set): 원소 중복X, 순서X
- 수학 연산과 동일
- 빈 집합은 set() < {}은 빈 dict
'Python' 카테고리의 다른 글
반복문 - while, for (0) | 2021.05.21 |
---|---|
조건문 - if (0) | 2021.05.18 |
작은 공간이지만 무한히 늘어날 지식 - 파이썬편 (0) | 2021.05.12 |
데이터 타입, 연산자 (3) 리스트, 튜플 (0) | 2021.05.11 |
데이터 타입, 연산자 (2) 문자열형 (0) | 2021.05.07 |