Chapter3 파이 채우기: 리스트, 튜플, 딕셔너리, 셋
이번 장에서 다룰 자료구조(data structure)는 분자(molecule)다. 이 말은 기본 타입들이 복잡한 형태로 결합된다는 것을 의미한다.
- booleans (True or False)
- integers
- floats
- strings
3.1 리스트와 튜플
이전 장에서 파이썬의 문자열은 문자의 시퀀스라는 것을 배웠다. 이번 장에서 리스트는 모든 것의 시퀀스라는 것을 알게 될 것이다.
파이썬에는 두 가지 다른 시퀀스 구조가 있다. 튜플(tuple)과 리스트(list)다. 문자열과는 달리, 이들 항목은 다른 타입이 될 수 있다. 다시 말해 각 요소는 어떤 객체도 될 수 있다.
파이썬은 왜 리스트와 튜플 모두를 포함하고 있을까? 튜플은 불변(immutable)한다. 튜플에 항목을 할당하고 나서, 이를 바꿀 수 없다. 리스트는 변경 가능(mutable)하다. 항목을 할당하고, 자유롭게 수정하거나 삭제할 수 있다.
3.2 리스트
리스트는 데이터를 순차적으로 파악하는 데 유용한다. 특히 내용의 순서가 바뀔 수 있다는 점에서 유용하다. 문자열과 달리 리스트는 변경 가능하다.
3.2.1 리스트 생성하기: [] 또는 list()
리스트는 0 혹은 그 이상의 요소로 만들어진다. 콤마(,)로 구분하고, 대괄호([])로 둘러싸여 있다.
>>> empty_list = []
>>> weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']
>>> big_birds = ['emu', 'ostrich', 'cassowary']
first_names = ['Graham', 'John', 'Terry', 'Terry', 'Michael']
# list() 함수로 빈 리스트 할당
>>> another_empty_list = list()
>>> another_empty_list
[]
요소들이 순서는 상관없이 유일한 값으로만 유지될 필요가 있다면, 리스트보다 셋(set)을 사용하는 것이 더 좋다.
3.2.2 다른 데이터 타입을 리스트로 변환하기: list()
>>> list('cat')
['c', 'a', 't']
# 튜플을 리스트로 변환
>>> a_tuple = ('ready', 'fire', 'aim')
>>> list(a_tuple)
['ready', 'fire', 'aim']
# split()은 문자열을 구분자로 나누어 리스트로 변환
>>> birthday = '1/6/1952'
>>> birthday.split('/')
['1', '6', '1952']
>>> splitme = 'a/b//c/d///e'
>>> splitme.split('/')
['a', 'b', '', 'c', 'd', '', '', 'e']
>>> splitme.split('//')
['a/b', 'c/d', '/e']
3.2.3 항목 얻기: [offset]
문자열과 마찬가지로 리스트는 오프셋으로 하나의 특정 값을 추출할 수 있다.
>>> marxes = ['Groucho', 'Chico', 'Harpo']
>>> marxes[0]
'Groucho'
>>> marxes[1]
'Chico'
>>> marxes[2]
'Harpo'
>>> marxes[-1]
'Harpo'
>>> marxes[-2]
'Chico'
>>> marxes[-3]
'Groucho'
# 오프셋의 위치가 리스트의 범위를 벗어날 경우 예외 발생
>>> marxes[5]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
3.2.4 리스트의 리스트
리스트는 리스트뿐만 아니라 다른 타입의 요소도 포함할 수 있다.
>>> small_birds = ['hummingbird', 'finch']
>>> extinct_birds = ['dodo', 'passenger pigeon', 'Norwegian Blue']
>>> carol_birds = [3, 'French hens', 2, 'turtledoves']
>>> all_birds = [small_birds, extinct_birds, 'macaw', carol_birds]
>>> all_birds
[['hummingbird', 'finch'], ['dodo', 'passenger pigeon', 'Norwegian Blue'], 'macaw', [3, 'French hens', 2, 'turtledoves']]
>>> all_birds[0]
['hummingbird', 'finch']
>>> all_birds[1]
['dodo', 'passenger pigeon', 'Norwegian Blue']
>>> all_birds[1][0]
'dodo'
3.2.5 항목 바꾸기: [offset]
>>> marxes
['Groucho', 'Chico', 'Harpo']
>>> marxes[2] = 'Wanda'
>>> marxes
['Groucho', 'Chico', 'Wanda']
리스트의 오프셋은 리스트에서 유효한 위치여야 한다.
문자열은 이러한 방식으로 변경할 수 없다. 왜냐하면 문자열은 불변한다. 리스트는 변경 가능하다. 리스트의 항목 수와 항목 내용을 바꿀 수 있다.
3.2.6 슬라이스로 항목 추출하기
>>> marxes = ['Groucho', 'Chico', 'Harpo']
>>> marxes[0:2]
['Groucho', 'Chico']
>>> marxes[::2]
['Groucho', 'Harpo']
>>> marxes[::-2]
['Harpo', 'Groucho']
>>> marxes[::-1]
['Harpo', 'Chico', 'Groucho']
3.2.7 리스트의 끝에 항목 추가하기: append()
append()는 리스트의 끝에 새 항목을 추가한다. 리스트는 변경 가능하기 때문에 이것을 추가할 수 있다.
>>> marxes.append('Zeppo')
>>> marxes
['Groucho', 'Chico', 'Harpo', 'Zeppo']
3.2.8 리스트 병합하기: extend() 또는 +=
>>> marxes = ['Groucho', 'Chico', 'Harpo', 'Zeppo']
>>> others = ['Gummo', 'Karl']
>>> marxes.extend(others)
>>> marxes
['Groucho', 'Chico', 'Harpo', 'Zeppo', 'Gummo', 'Karl']
>>> marxes = ['Groucho', 'Chico', 'Harpo', 'Zeppo']
>>> marxes += others
>>> marxes
['Groucho', 'Chico', 'Harpo', 'Zeppo', 'Gummo', 'Karl']
# append()를 사용하면 항목을 병합하지 않고, 하나의 리스트로 추가
>>> marxes = ['Groucho', 'Chico', 'Harpo', 'Zeppo']
>>> marxes.append(others)
>>> marxes
['Groucho', 'Chico', 'Harpo', 'Zeppo', ['Gummo', 'Karl']]
3.2.9 오프셋과 insert()로 항목 추가하기
append() 함수는 단지 리스트의 끝에 항목을 추가한다. 그러나 insert() 함수는 원하는 위치에 항목을 추가할 수 있다. 리스트의 끝을 넘는 오프셋은 append()처럼 끝에 항목을 추가한다. 예외를 던질 거라는 걱정은 하지 않아도 된다.
>>> marxes = ['Groucho', 'Chico', 'Harpo', 'Zeppo']
>>> marxes.insert(3, 'Gummo')
>>> marxes
['Groucho', 'Chico', 'Harpo', 'Gummo', 'Zeppo']
>>> marxes.insert(10, 'Karl')
>>> marxes
['Groucho', 'Chico', 'Harpo', 'Gummo', 'Zeppo', 'Karl']
3.2.10 오프셋으로 항목 삭제하기: del
>>> marxes
['Groucho', 'Chico', 'Harpo', 'Gummo', 'Zeppo', 'Karl']
>>> del marxes[-1]
>>> marxes
['Groucho', 'Chico', 'Harpo', 'Gummo', 'Zeppo']
>>> marxes[2]
'Harpo'
>>> del marxes[2]
>>> marxes
['Groucho', 'Chico', 'Gummo', 'Zeppo']
>>> marxes[2]
'Gummo'
del은 리스트 함수가 아니라 파이썬 구문이다.del은 할당(=)의 반대다. 이것은 객체로부터 이름을 분리하고, 객체의 메모리를 비워준다.
3.2.11 값으로 항목 삭제하기: remove()
리스트에서 삭제할 항목의 위치를 모르는 경우, remove()와 값으로 그 항목을 삭제할 수 있다.
>>> marxes
['Groucho', 'Chico', 'Gummo', 'Zeppo']
>>> marxes.remove('Gummo')
>>> marxes
['Groucho', 'Chico', 'Zeppo']
3.2.12 오프셋으로 항목을 얻은 후 삭제하기: pop()
pop()은 리스트에서 항목을 가져오는 동시에 그 항목을 삭제한다. 오프셋과 함께 pop()을 호출했다면 그 오프셋의 항목이 반환된다. 인자가 없다면 -1을 사용한다. pop(0)은 리스트의 시작(head)을 반환한다. 그리고 pop() 혹은 pop(-1)은 리스트의 끝(tail)을 반환한다.
>>> marxes = ['Groucho', 'Chico', 'Harpo', 'Zeppo']
>>> marxes.pop()
'Zeppo'
>>> marxes
['Groucho', 'Chico', 'Harpo']
>>> marxes.pop(1)
'Chico'
>>> marxes
['Groucho', 'Harpo']
append()로 새로운 항목을 끝에 추가한 뒤pop()으로 다시 마지막 항목을 제거했다면, LIFO(Last In First Out, 후입선출) 자료구조인 스택(stack)을 구현한 것이다.그리고
pop(0)을 사용했다면 FIFO(First In First Out, 선입선출) 자료구조인 큐(queue)를 구현한 것이다.
3.2.13 값으로 항목 오프셋 찾기: index()
>>> marxes = ['Groucho', 'Chico', 'Harpo', 'Zeppo']
>>> marxes.index('Chico')
1
3.2.14 존재여부 확인하기: in
>>> marxes
['Groucho', 'Chico', 'Harpo', 'Zeppo']
>>> 'Groucho' in marxes
True
>>> 'Bob' in marxes
False
3.2.15 값 세기: count()
>>> marxes
['Groucho', 'Chico', 'Harpo']
>>> marxes.count('Harpo')
1
>>> marxes.count('Bob')
0
>>> snl_skit = ['cheeseburger', 'cheeseburger', 'cheeseburger']
>>> snl_skit.count('cheeseburger')
3
3.2.16 문자열로 변환하기: join()
>>> marxes
['Groucho', 'Chico', 'Harpo']
>>> ', '.join(marxes)
'Groucho, Chico, Harpo'
join()은 문자열 메서드지, 리스트 메서드가 아니다. join()의 인자는 문자열 혹은 반복가능한(iterable) 문자열의 시퀀스다. 그리고 결과로 반환되는 값은 문자열이다.
>>> friends = ['Harry', 'Hermione', 'Ron']
>>> separator = ' * '
>>> joined = separator.join(friends)
>>> joined
'Harry * Hermione * Ron'
>>> separated = joined.split(separator)
>>> separated
['Harry', 'Hermione', 'Ron']
>>> separated == friends
True
3.2.17 정렬하기: sort()
값을 이용하여 리스트를 정렬한다. 파이썬은 두 가지 함수를 제공한다.
sort(): 리스트 자체를 내부적으로 정렬한다.sorted(): 리스트의 정렬된 복사본을 반환한다.
리스트의 항목이 숫자인 경우, 기본적으로 오름차순으로 정렬한다. 문자열인 경우, 알파벳순으로 정렬한다.
>>> marxes
['Groucho', 'Chico', 'Harpo']
>>> sorted_marxes = sorted(marxes)
>>> sorted_marxes
['Chico', 'Groucho', 'Harpo']
# 원본 리스트는 변하지 않는다.
>>> marxes
['Groucho', 'Chico', 'Harpo']
>>> marxes.sort()
>>> marxes
['Chico', 'Groucho', 'Harpo']
>>> numbers = [2, 1, 4.0, 3]
>>> numbers.sort()
>>> numbers
[1, 2, 3, 4.0]
# 내림차순 정렬
>>> numbers.sort(reverse=True)
>>> numbers
[4.0, 3, 2, 1]
3.2.18 항목 개수 얻기: len()
>>> marxes = ['Chico', 'Groucho', 'Harpo']
>>> len(marxes)
3
3.2.19 할당: =, 복사: copy()
>>> a = [1, 2, 3]
>>> a
[1, 2, 3]
>>> b = a
>>> a[0] = 'surprise'
>>> a
['surprise', 2, 3]
# 변수는 단지 이름(name)일 뿐이다.
# 할당한다는 의미는 값을 복사하는 것이 아니다. 그 이름은 객체 자신에 포함된 것이라기보다는 객체의 참조(reference)다.
# b는 단지 같은 리스트 객체의 a를 참조,
# 그러므로 두 변수 모두에 변경사항이 반영
>>> b
['surprise', 2, 3]
>>> b[0] = 'I hate surprise'
>>> b
['I hate surprise', 2, 3]
>>> a
['I hate surprise', 2, 3]
다음과 같은 방법을 이용하여 한 리스트를 새로운 리스트로 복사할 수 있다.
copy()함수list()변환 함수- 슬라이스
[:]
>>> a = [1, 2, 3]
>>> b = a.copy()
>>> c = list(a)
>>> d = a[:]
# b, c, d는 a의 복사본이다.
# 이들은 자신만의 값을 가진 새로운 객체다.
>>> a[0] = 'integer lists are boring'
>>> a
['integer lists are boring', 2, 3]
>>> b
[1, 2, 3]
>>> c
[1, 2, 3]
>>> d
[1, 2, 3]
3.3 튜플
리스트와 다르게 튜플은 불변한다. 튜플은 정의한 후에는 추가, 삭제, 수정을 할 수 없다는 것을 의미한다. 그러므로 튜플은 상수의 리스트라고 할 수 있다.
3.3.1 튜플 생성하기: ()
>>> empty_tuple = ()
>>> empty_tuple
()
# 하나 이상의 요소가 있는 튜플을 만들기 위해서는
# 각 요소 뒤에 콤마(,)를 붙인다.
>>> one_marx = 'Groucho',
>>> one_marx
('Groucho',)
# 두 개 이상의 요소가 있을 경우,
# 마지막 요소에는 콤마를 붙이지 않는다.
>>> marx_tuple = 'Groucho', 'Chico', 'Harpo'
>>> marx_tuple
('Groucho', 'Chico', 'Harpo')
# 괄호를 적어주면, 더 명시적이다.
>>> marx_tuple = ('Groucho', 'Chico', 'Harpo')
>>> marx_tuple
('Groucho', 'Chico', 'Harpo')
# tuple() : 다른 객체를 튜플로 만든다.
>>> marx_list = ['Groucho', 'Chico', 'Harpo']
>>> tuple(marx_list)
('Groucho', 'Chico', 'Harpo')
튜플을 정의할 때 괄호(())가 필요 없다. 뒤에 콤마가 붙는다는 것은 튜플을 정의한다는 뜻이다.
튜플 언패킹(tuple unpacking)
>>> a, b, c = marx_tuple
>>> a
'Groucho'
>>> b
'Chico'
>>> c
'Harpo'
# 한 문장에서 값을 교환하기 위해 임시 변수를 사용하지 않고 튜플을 사용할 수 있다.
>>> password = 'swordfish'
>>> icecream = 'tuttifrutti'
>>> password, icecream = password, icecream
>>> password
'swordfish'
>>> icecream
'tuttifrutti'
3.3.2 튜플과 리스트
튜플은 리스트의 append(), insert() 등과 같은 함수가 없고, 함수의 수가 매우 적다. 그러면 왜 튜플을 사용할까?
- 튜플은 더 적은 공간을 사용한다.
- 실수로 튜플의 항목이 손상될 염려가 없다.
- 튜플을 딕셔너리의 키로 사용할 수 있다.
- 네임드 튜플(named tuple)은 객체의 단순한 대안이 될 수 있다.
- 함수의 인자들은 튜플로 전달된다.
일반적으로 리스트와 딕셔너리를 더 많이 사용한다.
3.4 딕셔너리
딕셔너리(dictionary)는 리스트와 비슷하다. 다른 점은 항목의 순서를 따지지 않으며, 0또는 1과 같은 오프셋으로 항목을 선택할 수 없다. 대신 값(value)에 상응하는 고유한 키(key)를 지정한다. 딕셔너리는 변경 가능하므로 키-값 요소를 추가, 삭제, 수정할 수 있다.
다른 언어에서는 딕셔너리를 associative array, hash, hashmap 이라고 부른다.
3.4.1 딕셔너리 생성하기:{}
>>> empty_dict = {}
>>> empty_dict
{}
>>> bierce = {
... "day" : "A period of twenty-four hours, mostly misspent",
... "positive" : "Mistaken at the top of one's voice",
... "misfortune" : "The kind of fortune that never misses",
... }
>>> bierce
{'day': 'A period of twenty-four hours, mostly misspent', 'misfortune': 'The kind of fortune that never misses', 'positive': "Mistaken at the top of one's voice"}
3.4.2 딕셔너리로 변환하기: dict()
딕셔너리의 키 순서는 임의적이다.
>>> lol = [['a', 'b'], ['c', 'd'], ['e', 'f']]
>>> dict(lol)
{'c': 'd', 'a': 'b', 'e': 'f'}
>>> lot = [('a', 'b'), ('c', 'd'), ('e', 'f')]
>>> dict(lot)
{'c': 'd', 'a': 'b', 'e': 'f'}
>>> tol = (['a', 'b'], ['c', 'd'], ['e', 'f'])
>>> dict(tol)
{'c': 'd', 'a': 'b', 'e': 'f'}
>>> los = ['ab', 'cd', 'ef']
>>> dict(los)
{'c': 'd', 'a': 'b', 'e': 'f'}
3.4.3 항목 추가/변경하기: [key]
리스트와 달리 딕셔너리를 할당할 때는 인덱스의 범위 지정이 벗어났다는 예외에 대해 걱정할 필요가 없다.
>>> pythons = {
... 'Chapman': 'Graham',
... 'Cleese': 'John',
... 'Idle': 'Eric',
... 'Jones': 'Terry',
... 'Palin': 'Michael',
... }
>>> pythons
{'Idle': 'Eric', 'Cleese': 'John', 'Chapman': 'Graham', 'Palin': 'Michael', 'Jones': 'Terry'}
>>> pythons['Gilliam'] = 'Gerry'
>>> pythons
{'Idle': 'Eric', 'Cleese': 'John', 'Chapman': 'Graham', 'Palin': 'Michael', 'Gilliam': 'Gerry', 'Jones': 'Terry'}
>>> pythons['Gilliam'] = 'Terry'
>>> pythons
{'Idle': 'Eric', 'Cleese': 'John', 'Chapman': 'Graham', 'Palin': 'Michael', 'Gilliam': 'Terry', 'Jones': 'Terry'}
딕셔너리 키들은 반드시 유일해야 한다. 만약 같은 키를 두 번 이상 사용하면 마지막 값이 승리한다.
>>> some_python = {
... 'Graham': 'Chapman',
... 'John': 'Cleese',
... 'Eric': 'Idle',
... 'Terry': 'Gilliam',
... 'Michael': 'Palin',
... 'Terry': 'Jones',
... }
>>> some_python
{'Eric': 'Idle', 'John': 'Cleese', 'Michael': 'Palin', 'Graham': 'Chapman', 'Terry': 'Jones'}
3.4.4 딕셔너리 결합하기: update()
update() 함수는 한 딕셔너리의 키와 값들을 복사해서 다른 딕셔너리에 붙여준다.
>>> pythons
{'Idle': 'Eric', 'Cleese': 'John', 'Chapman': 'Graham', 'Palin': 'Michael', 'Gilliam': 'Terry', 'Jones': 'Terry'}
>>> others = {'Marx': 'Groucho', 'Howard': 'Moe'}
>>> pythons.update(others)
>>> pythons
{'Idle': 'Eric', 'Cleese': 'John', 'Chapman': 'Graham', 'Howard': 'Moe', 'Marx': 'Groucho', 'Palin': 'Michael', 'Gilliam': 'Terry', 'Jones': 'Terry'}
>>> first = {'a': 1, 'b': 2}
>>> second = {'b': 'platypus'}
>>> first.update(second)
>>> first
{'a': 1, 'b': 'platypus'}
3.4.5 항목 삭제하기: [key]와 del
>>> del pythons['Marx']
>>> pythons
{'Idle': 'Eric', 'Cleese': 'John', 'Chapman': 'Graham', 'Howard': 'Moe', 'Palin': 'Michael', 'Gilliam': 'Terry', 'Jones': 'Terry'}
>>> del pythons['Howard']
>>> pythons
{'Idle': 'Eric', 'Cleese': 'John', 'Chapman': 'Graham', 'Palin': 'Michael', 'Gilliam': 'Terry', 'Jones': 'Terry'}
3.4.6 모든 항목 삭제하기: clear()
>>> pythons.clear()
>>> pythons
{}
>>> others = {}
>>> others
{}
3.4.7 키(key)를 위한 테스트: in
딕셔너리에 키가 존재하는지 알고 싶다면 in을 사용한다.
>>> pythons = {
... 'Chapman': 'Graham',
... 'Cleese': 'John',
... 'Jones': 'Terry',
... 'Palin': 'Michael'
... }
>>> 'Chapman' in pythons
True
>>> 'Palin' in pythons
True
>>> 'Gilliam' in pythons
False
3.4.8 항목 얻기: [key]
>>> pythons['Cleese']
'John'
# 딕셔너리에 키가 존재하지 않으면 예외발생
>>> pythons['Marx']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'Marx'
위의 문제를 피하는 방법이 있다. 첫 번째 in으로 키에 대해 테스트를 실행하는 것이다. 두 번째 방법은 딕셔너리의 get() 함수를 사용하는 것이다. 이 함수는 딕셔너리, 키, 옵션값을 사용한다.
# 키가 존재할 경우, 그 값을 얻는다.
>>> pythons.get('Cleese')
'John'
# 키가 존재하지 않을 경우, 옵션값을 지정해 이를 출력
>>> pythons.get('Marx', 'Not a Python')
'Not a Python'
# 옵션값을 지정하지 않으면 None을 얻는다.
>>> pythons.get('Marx')
>>>
3.4.9 모든 키 얻기: keys()
딕셔너리의 모든 키를 가져오기 위해서는 keys()를 사용한다.
>>> signals = {'green': 'go', 'yellow': 'go faster', 'red': 'smile for the camera'}
>>> signals.keys()
dict_keys(['green', 'yellow', 'red'])
순회가능한(iterable)
dict_keys()를 반환한다. 이것은 아주 큰 딕셔너리에 유용하다. 사용되지 않을 리스트를 생성하고 저장하기 위한 메모리와 시간을 소비하지 않기 때문이다.이것을 실제 리스트로 쓰고 싶을 때가 있다. 파이썬 3에서는
list()를 호출해서 dict_keys 객체를 리스트로 변환할 수 있다.>>> list(signals.keys()) ['green', 'yellow', 'red']
3.4.10 모든 값 얻기: values()
>>> list(signals.values())
['go', 'go faster', 'smile for the camera']
3.4.11 모든 쌍의 키-값 얻기: items()
# 각 키와 값은 튜플로 반환
>>> list(signals.items())
[('green', 'go'), ('yellow', 'go faster'), ('red', 'smile for the camera')]
3.4.12 할당: =, 복사: copy()
>>> signals
{'green': 'go', 'yellow': 'go faster', 'red': 'smile for the camera'}
>>> save_signals = signals
>>> signals['blue'] = 'confuse everyone'
>>> save_signals
{'green': 'go', 'yellow': 'go faster', 'blue': 'confuse everyone', 'red': 'smile for the camera'}
>>> signals = {'green': 'go', 'yellow': 'go faster', 'red': 'smile for the camera'}
>>> original_signals = signals.copy()
>>> signals['blue'] = 'confuse everyone'
>>> signals
{'green': 'go', 'yellow': 'go faster', 'blue': 'confuse everyone', 'red': 'smile for the camera'}
>>> original_signals
{'green': 'go', 'yellow': 'go faster', 'red': 'smile for the camera'}
3.5 셋
셋(set)은 값은 버리고 키만 남은 딕셔너리와 같다. 딕셔너리와 마찬가지로 각 키는 유일해야 한다. 어떤 것이 존재하는지 여부만 판단하기 위해서는 셋을 사용한다. 그리고 키에 어떤 정보를 첨부해서 그 결과를 얻고 싶으면 딕셔너리를 사용한다.
3.5.1 셋 생성하기: set()
딕셔너리의 키와 마찬가지로 셋은 순서가 없다.
>>> empty_set = set()
>>> empty_set
set()
>>> even_number = {0, 2, 4, 6, 8}
>>> even_number
{8, 0, 2, 4, 6}
>>> odd_number = {1, 3, 5, 7, 9}
>>> odd_number
{9, 1, 3, 5, 7}
{}는 빈 딕셔너리를 생성한다. 이것이 인터프리터가 빈 셋을{}대신set()으로 출력하는 이유이기도 하다. 왜 그럴까? 파이썬에서 딕셔너리가 먼저 등장해서 중괄호를 이미 차지하고 있었기 때문이다.
3.5.2 데이터 타입 변환하기: set()
리스트, 문자열, 튜플, 딕셔너리로부터 중복된 값을 버린 셋을 생성할 수 있다.
>>> set('letters')
{'l', 't', 'e', 'r', 's'}
>>> set(['Dasher', 'Dancer', 'Prancer', 'Mason-Dixon'])
{'Dasher', 'Prancer', 'Dancer', 'Mason-Dixon'}
>>> set(('Ummagumma', 'Echoes', 'Atom Heart Mother'))
{'Ummagumma', 'Echoes', 'Atom Heart Mother'}
>>> set({'apple': 'red', 'orange': 'orange', 'cherry': 'red'})
{'cherry', 'orange', 'apple'}
3.5.3 in으로 값 멤버십 테스트하기
이것은 일반적으로 사용되는 셋의 용도다.
# 딕셔너리 안에 여러개의 키-값 쌍들이 있다.
# 셋은 값들의 시퀀스이다.
>>> drinks = {
... 'martini': {'vodka', 'vermouth'},
... 'black russian': {'vodka', 'kahlua'},
... 'white russian': {'cream', 'kahlua', 'vodka'},
... 'manhattan': {'rye', 'vermouth', 'bitters'},
... 'screwdriver': {'orange juice', 'vodka'}
... }
>>> for name, contents in drinks.items():
... if 'vodka' in contents:
... print(name)
...
screwdriver
black russian
white russian
martini
3.5.4 콤비네이션과 연산자
셋값의 조합(combination)을 어떻게 확인할까?
셋 교집합 연산자(set intersection operator)인 앰퍼샌드(&)를 사용한다.
>>> for name, contents in drinks.items():
... if contents & {'vermouth', 'orange juice'}:
... print(name)
...
manhattan
screwdriver
martini
& 연산자 결과가 없다면, False로 간주되는 빈 셋을 반환한다.
>>> bruss = drinks['black russian']
>>> wruss = drinks['white russian']
>>> a = {1, 2}
>>> b = {2, 3}
# 교집합(intersection) : 양쪽 셋에 모두 들어있는 멤버
>>> a & b
{2}
>>> a.intersection(b)
{2}
>>> bruss & wruss
{'kahlua', 'vodka'}
# 합집합(union) : 각 셋의 멤버 모두
>>> a | b
{1, 2, 3}
>>> a.union(b)
{1, 2, 3}
>>> bruss | wruss
{'kahlua', 'cream', 'vodka'}
>>> bruss.union(wruss)
{'kahlua', 'cream', 'vodka'}
# 차집합(difference) : 첫 번째 셋에는 있지만 두 번째 셋에는 없는 멤버
>>> a - b
{1}
>>> a.difference(b)
{1}
>>> bruss - wruss
set()
>>> wruss - bruss
{'cream'}
# 대칭 차집합(exclusive) : 한 쪽 셋에는 들어 있지만 양쪽 모두에 들어 있지 않은 멤버
>>> a ^ b
{1, 3}
>>> a.symmetric_difference(b)
{1, 3}
>>> bruss ^ wruss
{'cream'}
# 부분집합(subset)
>>> a <= b
False
>>> a.issubset(b)
False
>>> bruss <= wruss
True
# 모든 셋은 자신의 서브셋이다.
>>> a <= a
True
>>> a.issubset(a)
True
# 진부분집합(proper subset) : 첫 번째 셋이 두 번째 셋의 진부분집합이 되려면,
# 두 번째 셋에는 첫 번째 셋의 모든 멤버를 포함한 그 이상의 멤버가 있어야 한다.
>>> a < b
False
>>> a < a
False
>>> bruss < wruss
True
# superset : subset의 반대이다.
>>> a >= b
False
>>> a.issuperset(b)
False
>>> wruss >= bruss
True
# 모든 셋은 자신의 슈퍼셋이다.
>>> a >= a
True
>>> a.issuperset(a)
True
# proper superset: 첫 번째 셋이 두 번째 셋의 프로퍼 슈퍼셋이 되려면,
# 첫 번째 셋에는 두 번째 셋의 모든 멤버를 포함한 그 이상의 멤버가 있어야 한다.
>>> a > b
False
>>> wruss > bruss
True
# 모든 셋은 자신의 프로퍼 슈퍼셋이 될 수 없다.
>>> a > a
False
3.6 자료구조 비교하기
[]: 리스트,: 튜플{}: 딕셔너리
>>> marx_list = ['Groucho', 'Chico', 'Harpo']
>>> marx_tuple = ('Groucho', 'Chico', 'Harpo')
>>> marx_dict = {'Harpo': 'harp', 'Chico': 'piano', 'Groucho': 'banjo'}
>>> marx_list[2]
'Harpo'
>>> marx_tuple[2]
'Harpo'
>>> marx_dict['Harpo']
'harp'
리스트와 튜플은 대괄호에 들어가는 값이 정수 오프셋이고, 딕셔너리의 경우에는 키다.
3.7 자료구조를 더 크게
내장된 자료구조를 결합해서 더 크고 복잡한 자료구조를 만들 수 있다.
>>> marxes = ['Groucho', 'Chico', 'Harpo']
>>> pythons = ['Chapman', 'Cleese', 'Gilliam', 'Jones', 'Palin']
>>> stooges = ['Moe', 'Curly', 'Larry']
# 튜플의 각 요소의 리스트다.
>>> tuple_of_lists = marxes, pythons, stooges
>>> tuple_of_lists
(['Groucho', 'Chico', 'Harpo'], ['Chapman', 'Cleese', 'Gilliam', 'Jones', 'Palin'], ['Moe', 'Curly', 'Larry'])
# 세 리스트를 한 리스트에 포함
>>> list_of_lists = [marxes, pythons, stooges]
>>> list_of_lists
[['Groucho', 'Chico', 'Harpo'], ['Chapman', 'Cleese', 'Gilliam', 'Jones', 'Palin'], ['Moe', 'Curly', 'Larry']]
# 리스트의 딕셔너리
>>> dict_of_lists = {'Pythons': pythons, 'Stooges': stooges, 'Marxes': marxes}
>>> dict_of_lists
{'Pythons': ['Chapman', 'Cleese', 'Gilliam', 'Jones', 'Palin'], 'Stooges': ['Moe', 'Curly', 'Larry'], 'Marxes': ['Groucho', 'Chico', 'Harpo']}
제한사항이 있다면 데이터 타입 그 자체다. 예를 들어 딕셔너리의 키는 불변하기 때문에 리스트, 딕셔너리, 셋은 다른 딕셔너리의 키가 될 수 없다. 그러나 튜플은 딕셔너리의 키가 될 수 있다.
>>> houses = {
...: (44.79, -93.14, 285): 'My House',
...: (38.89, -77.03, 13): 'The White House'
...: }