파이썬 튜플 비교 - paisseon tyupeul bigyo

리스트는 대괄호 [ ]를, 튜플은 소괄호 ( )를, 딕셔너리는 중괄호 { }를 사용해서 나타냅니다. a라는 이름의 리스트, b라는 이름의 튜플, c라는 이름의 딕셔너리를 만들어보겠습니다. 

 

파이썬 튜플 비교 - paisseon tyupeul bigyo

 

리스트와 튜플 비교

리스트와 튜플은 표면상으로는 매우 유사한데, 한 가지 중요한 차이가 있습니다. 튜플의 경우 요소를 변경하거나 삭제할 수 없습니다. 반면 리스트에서는 가능하고요. 그것을 전문 용어로 mutable, immutable이라고 부릅니다. mutable은 값을 변경할 수 있다는 뜻이고, immutable은 변경할 수 없다는 뜻입니다. 

 

파이썬 튜플 비교 - paisseon tyupeul bigyo

 

위와 같이 리스트에서는 요소를 변경하는 것이 가능합니다. 첫번째 요소 1이 6으로 바뀌었죠. 또한 다음과 같이 특정 요소를 삭제하는 것도 가능합니다. 

 

파이썬 튜플 비교 - paisseon tyupeul bigyo

 

6으로 바뀌었던 첫번째 요소가 제거되었습니다. 

 

이번에는 튜플에서 위와 같은 방식으로 요소를 변경하고 제거하는 것이 가능한 지 확인해보겠습니다. 

 

파이썬 튜플 비교 - paisseon tyupeul bigyo

 

에러메시지가 떴습니다. 읽어보니, 튜플은 아이템 할당을 지원하지 않는다고 나와있죠? 인덱스를 통해 값을 변경하지 못하도록 해놨다는 뜻으로 해석할 수 있을 것 같습니다. 이번에는 요소를 제거해보겠습니다. 

 

파이썬 튜플 비교 - paisseon tyupeul bigyo

 

이번에도 역시 에러메시지가 떴습니다. 튜플은 아이템 deletion, 즉 삭제를 지원하지 않는다고 하죠? 

 

리스트, 튜플과 딕셔너리 비교

리스트와 튜플이 어떤 값만을 담을 수 있었다면, 딕셔너리는 키(key)라는 것과 그 키에 해당하는 값(value)을 담을 수 있습니다. 

 

파이썬 튜플 비교 - paisseon tyupeul bigyo

 

보시는 것처럼 딕셔너리를 활용하면 블로그명이라는 키에는 비스카이비전이라는 값을, 필자명이라는 키에는 심교훈이라는 값을, ... 담을 수 있습니다. 키가 단어라면, 

 

딕셔너리는 리스트, 튜플과 달리 순서가 중요하지 않습니다. 리스트, 튜플은 첫번째 요소, 두번째 요소, 세번째 요소 등 순서가 있는데, 딕셔너리에는 순서가 없습니다. 따라서 인덱스로 접근이 불가하고, 키로 접근해야 합니다. 

엄밀하게 말할 때, 함수는 오직 하나의 값만을 돌려줄 수 있습니다만, 만약 그 값이 튜플이라면, 효과는 여러 개의 값을 돌려주는 것과 마찬가지 입니다. 예를 들어, 만약 두 정수로 나눗셈을 해서, 몫과 나머지를 계산하고자 한다면, x/y 를 계산 한 후에 x%y 를 계산하는 것은 비효율적입니다. 두 값을 함께 계산하는 것이 더 좋습니다.

내장 함수 divmod는 두 개의 인자를 받아들여서, 두 값, 몫과 나머지, 의 튜플을 돌려줍니다. 결과를 튜플로 저장할 수 있습니다:

>>> t = ('a', 'b', 'c', 'd', 'e')
5

또는 각 요소들을 따로 저장하기 위해 튜플 대입을 사용할 수 있습니다:

>>> t = ('a', 'b', 'c', 'd', 'e')
6

여기에 튜플을 돌려주는 함수의 예가 있습니다:

>>> t = ('a', 'b', 'c', 'd', 'e')
7

max 와 min은 시퀀스에서 가장 큰 요소와 가장 작은 요소를 찾는 내장함수입니다. min_max 는 둘 다 계산해서 두 값의 튜플을 돌려줍니다.

가변길이 인자 튜플

함수는 고정되지 않은 개수의 인자를 받아들일 수 있습니다. 로 시작하는 매개변수 이름은 인자들을 튜플로 수집(gather)합니다. 예를 들어, printall은 임의의 개수의 인자들을 받아들여서, 그들을 인쇄합니다:

>>> t = ('a', 'b', 'c', 'd', 'e')
8

수집 매개변수는 여러분이 원하는 어떤 이름이건 될 수 있습니다만, args가 자주 쓰입니다. 함수는 이런 식으로 동작합니다:

>>> t = ('a', 'b', 'c', 'd', 'e')
9

수집의 반대는 산개(scatter)입니다. 값의 시퀀스가 있고 함수에 여러 개의 인자로 전달하고 싶다면, 연산자를 사용할 수 있습니다. 예를 들어, divmod 는 정확히 두 개의 인자를 받아들입니다; 튜플은 사용할 수 없습니다:

>>> t1 = 'a',
>>> type(t1)
<type 'tuple'>
0

그러나 튜플을 산개한다면, 동작합니다:

>>> t1 = 'a',
>>> type(t1)
<type 'tuple'>
1

[연습 12.1.]

여러 내장함수들은 가변길이 인자 튜플을 사용합니다. 예를 들어, max와 min은 몇 개의 인자든 받아들일 수 있습니다:

>>> t1 = 'a',
>>> type(t1)
<type 'tuple'>
2

하지만 sum은 그렇지 않습니다.

>>> t1 = 'a',
>>> type(t1)
<type 'tuple'>
3

몇 개의 인자든 받아들여서 그들의 합을 돌려주는 함수 sumall을 작성하세요.

리스트와 튜플

zip은 두 개나 그 이상의 시퀀스를 받아들여서 튜플들의 리스트로 “집(zip)” 하는데, 각 튜플은 각 시퀀스들로부터 요소를 하나씩 받아 만들어집니다. 파이썬 3에서, zip은 튜플의 이터레이터(iterator)를 돌려줍니다만, 대부분의 목적에서 이터레이터는 리스트처럼 동작합니다.

이 예는 문자열과 리스트를 zip 합니다:

>>> t1 = 'a',
>>> type(t1)
<type 'tuple'>
4

결과는 튜플의 리스트인데, 각 튜플은 문자열에서 온 문자와 리스트의 해당 요소로 구성됩니다.

만약 시퀀스들의 길이가 같지 않다면, 결과는 가장 짧은 것의 길이를 갖습니다.

>>> t1 = 'a',
>>> type(t1)
<type 'tuple'>
5

튜플의 리스트를 탐색하는 for 순환에서 튜플 대입을 사용할 수 있습니다:

>>> t1 = 'a',
>>> type(t1)
<type 'tuple'>
6

매 순환 마다, 파이썬은 리스트의 다음 튜플을 선택하고 요소들을 letter 와 number에 대입합니다. 이 순환의 출력은 이렇습니다:

>>> t1 = 'a',
>>> type(t1)
<type 'tuple'>
7

zip 과 for 와 튜플 대입을 결합하면, 두 (또는 그 이상의) 시퀀스를 동시에 탐색하는 유용한 관용구를 얻습니다. 예를 들어, has_match 는 두 시퀀스, t1 과 t2,를 받아들여서 t1[i] == t2[i] 이 성립하는 지수 i가 존재하면 True를 돌려줍니다:

>>> t1 = 'a',
>>> type(t1)
<type 'tuple'>
8

시퀀스의 요소와 그들의 지수를 탐색할 필요가 있으면, 내장 함수 enumerate를 사용할 수 있습니다:

>>> t1 = 'a',
>>> type(t1)
<type 'tuple'>
9

이 순환의 출력은 이렇습니다:

>>> t1 = 'a',
>>> type(t1)
<type 'tuple'>
7

앞의 결과와 같지요.

딕셔너리와 튜플

[dictuple]

딕셔너리에는 items 이라는 메쏘드가 있어서 튜플의 리스트를 돌려주는데, 각 튜플은 키-값 쌍입니다.

>>> t2 = ('a')
>>> type(t2)
<type 'str'>
1

딕셔너리라는 것에서 짐작하듯이, 항목들은 특별한 순서를 갖고 있지 않습니다. 파이썬 3에서, items는 이터레이터를 돌려주는데, 많은 목적에서 이터레이터는 리스트처럼 행동합니다.

다른 방향으로 가서, 튜플의 리스트를 새 딕셔너리를 초기화하는데 사용할 수 있습니다:

>>> t2 = ('a')
>>> type(t2)
<type 'str'>
2

dict 와 zip 을 결합하면 딕셔너리를 만드는 간결한 방법을 얻습니다:

>>> t2 = ('a')
>>> type(t2)
<type 'str'>
3

딕셔너리 메쏘드 update 또한 튜플의 리스트를 받아들이는데, 기존의 딕셔너리에 그들, 키-값 쌍으로,을 추가합니다.

items 와 튜플 대입과 for를 결합하면, 딕셔너리의 키와 값들을 탐색하는 관용구를 얻습니다:

>>> t2 = ('a')
>>> type(t2)
<type 'str'>
4

이 순환의 출력은 이렇습니다:

>>> t2 = ('a')
>>> type(t2)
<type 'str'>
5

또 만났네요.

튜플은 (주로 리스트를 사용할 수 없기 때문에) 딕셔너리의 키로 자주 사용됩니다. 예를 들어, 전화번호부는 성, 이름 쌍을 전화 번호로 대응시킵니다. last, first, number 를 정의했다고 가정할 때, 이렇게 쓸 수 있습니다:

>>> t2 = ('a')
>>> type(t2)
<type 'str'>
6

대괄호 안의 표현식은 튜플입니다. 이 딕셔너리를 탐색하기 위해 튜플 대입을 사용할 수 있습니다.

>>> t2 = ('a')
>>> type(t2)
<type 'str'>
7

이 순환은 directory 의 키를 탐색하는데, 키가 튜플입니다. 각 튜플을 last 와 first에 대입한 다음, 이름과 그에 대응하는 전화번호를 인쇄합니다.

상태도에 튜플을 표시하는 두 가지 방법이 있습니다. 더 상세한 버전은 리스트에서와 마찬가지로 지수와 요소들을 보여줍니다. 예를 들어, 튜플 ('Cleese', 'John') 는 그림 [fig.tuple1] 처럼 표시됩니다.

파이썬 튜플 비교 - paisseon tyupeul bigyo

[fig.tuple1]

그러나 더 큰 다이어그램에서는 자세한 내용을 생략하고 싶을 겁니다. 예를 들어, 전화번호부의 다이어그램은 그림 [fig.dict2] 처럼 표시할 수 있습니다.

파이썬 튜플 비교 - paisseon tyupeul bigyo

[fig.dict2]

여기에서 튜플은 도식적 약기로 파이썬 문법을 사용해서 표시됩니다.

다이어그램에 나오는 번호는 BBC 의 고객상담번호이니 전화 걸지 말아주세요.

튜플 비교하기

비교 연산자는 튜플과 다른 시퀀스들에 사용할 수 있습니다; 파이썬은 각 시퀀스의 첫 번째 요소를 비교하는 것으로 시작합니다. 만약 그들이 같다면, 다음 요소로 넘어가고, 다른 요소들을 발견할 때까지 이런 과정을 계속합니다. (설사 그들이 아주 크다고 할지라도) 뒤에 남은 요소들은 고려하지 않습니다.

>>> t2 = ('a')
>>> type(t2)
<type 'str'>
8

sort 함수도 같은 식으로 동작합니다. 기본적으로 첫 번째 요소로 정렬하지만, 같은 경우에 두 번째 요소를 사용하고, 이런 식으로 계속됩니다.

이 기능은 DSU 라고 부르는 패턴을 만들어내는데, 다음의 줄임 말입니다.

Decorate시퀀스의 요소 앞에 하나나 그 이상의 키를 붙인 튜플의 목록을 만듦으로써 시퀀스를 장식(decorate)하고,Sort튜플의 리스트를 정렬(sort) 한 후,Undecorate시퀀스의 정렬된 요소들을 추출해 냄으로써 장식을 해제(undecorate)합니다.

[DSU]

예를 들어, 가장 긴 것에서 짧은 것 순으로 정렬하고 싶은 단어들의 리스트가 있다고 합시다:

>>> t2 = ('a')
>>> type(t2)
<type 'str'>
9

첫 번째 순환은 튜플이 리스트를 만드는데, 각 튜플은 그 길이가 앞에 나오는 단어입니다.

sort는 첫 번째 요소 length 를 먼저 비교하고, 같을 경우에만 두 번째 요소를 고려합니다. 키워드 인자 reverse=True는 sort에게 내림차순임을 지시합니다.

두 번째 순환은 튜플의 리스트를 탐색하면서, 길이의 내림차순으로 정렬된 단어들의 리스트를 만듭니다.

[연습 12.2.]

이 예에서, 길이가 같을 때는 단어를 비교하기 때문에, 같은 길이의 단어들은 알파벳 역순으로 나오게 됩니다. 다른 응용에서 길이가 같을 때는 임의의 순서로 나오게 하고 싶을 수 있습니다. 이 예를 길이가 같은 단어들이 임의의 순서로 나타나게 수정하세요. 힌트: random 모듈의 random 함수를 보세요. 답: http://thinkpython.com/code/unstable_sort.py.

시퀀스의 시퀀스

튜플의 리스트에 집중했습니다만, 이 장의 거의 모든 예는 리스트의 리스트, 튜플의 튜플, 리스트의 튜플에 대해서도 동작합니다. 모든 가능한 조합을 나열하는 대신데, 시퀀스의 시퀀스에 대해서 말하는 게 더 간편합니다.

많은 경우에, 다른 종류의 시퀀스 (문자열, 리스트, 튜플)들은 바꿔 사용될 수 있습니다. 그렇다면 어떻게 그리고 왜 특정한 한가지 종류를 선택할까요?

자명한 것부터 시작해보면, 문자만을 요소로 가질 수 있는 문자열은 다른 시퀀스들보다 더 제한적입니다. 수정 불가능하기도 합니다. (새 문자열을 만드는 대신) 문자열에 있는 문자를 변경하는 능력이 필요하다면, 문자의 리스트를 사용하고 싶을 겁니다.

리스트는 튜플보다 자주 쓰이는데, 대부분 수정 가능하기 때문입니다. 그러나 튜플을 선호할 몇 가지 경우가 있습니다:

  1. return 문과 같은 몇몇 경우에 리스트 대신에 튜플을 만드는 것이 문법적으로 더 간단합니다. 다른 경우에는 아마도 리스트를 선호할겁니다.
  2. 시퀀스를 딕셔너리의 키로 사용하려면, 튜플이나 문자열 같이 수정 불가능한 형을 사용해야만 합니다.
  3. 시퀀스를 함수에 인자로 전달하고 있다면, 튜플을 사용하는 것이 애일리어싱으로 인한 예상치 못한 행동의 위험을 줄입니다.

튜플은 수정불가능하기 때문에, 기존의 리스트를 수정하는 sort 와 reverse 같은 메쏘드들을 제공하지 않습니다. 그러나 파이썬은 내장 함수 sorted 와 reversed를 제공하는데, 모든 종류의 시퀀스를 인자로 받아들여서 순서를 바꾼 새 리스트를 돌려줍니다.

디버깅

리스트, 딕셔너리, 튜플은 자료 구조라는 포괄적인 용어로 알려져 있습니다; 이 장에서 우리는 튜플의 리스트나 튜플을 키로 하고 리스트를 값으로 갖는 딕셔너리와 같은 복합 자료 구조를 보기 시작했습니다. 복합 자료 구조는 유용합니다만, 제가 형태(shape) 오류라고 부르는 것에 취약한데, 자료 구조가 잘못된 형, 크기, 조성(composition)을 가짐으로써 유발되는 오류를 뜻합니다. 예를 들어, 여러분이 정수 하나가 들어있는 리스트를 기대하고 있을 때 제가 그냥 (리스트에 들어있지 않은) 정수만 준다면, 뜻한 데로 동작하지 않을 것입니다.

이런 종류의 오류를 디버깅하기 쉽도록 하기 위해, structshape 이라는 함수를 제공하는 structshape 모듈을 작성했는데, 모든 종류의 자료 구조를 인자로 받아서 형태를 요약하는 문자열을 돌려줍니다. http://thinkpython.com/code/structshape.py에서 다운로드 할 수 있습니다.

여기에 단순한 리스트에 대한 결과가 있습니다:

>>> t = tuple()
>>> print t
()
0

더 장식적인 프로그램은 “list of 3 ints,” 라고 쓸 수 있겠지만, 복수형을 다루지 않는 것이 더 쉽습니다. 여기에 리스트의 리스트가 있습니다:

>>> t = tuple()
>>> print t
()
1

만약 리스트의 요소들이 같은 형이 아니라면, structshape 은 순서를 유지하면서 형 별로 묶습니다:

>>> t = tuple()
>>> print t
()
2

여기에 튜플의 리스트가 있습니다:

>>> t = tuple()
>>> print t
()
3

그리고 여기에 정수를 문자열로 대응시키는 세 항목을 갖는 딕셔너리가 있습니다.

>>> t = tuple()
>>> print t
()
4

여러분의 자료 구조를 쫓는데 어려움이 있다면, structshape 가 도움이 될 수 있습니다.

용어

튜플 tuple:요소들의 수정 불가능한 시퀀스.튜플 대입 tuple assignment:우변에 시퀀스가, 좌변에 변수들의 튜플이 오는 대입. 우변이 환산된 후에 요소들이 좌변의 변수들에 대입됩니다.수집 gather:가변길이 인자 튜플을 만드는 연산.산개 scatter:시퀀스를 인자 목록으로 처리하는 연산.DSU:“decorate-sort-undecorate”의 약어로, 튜플 리스트를 만든 후 정렬하고 결과의 일부를 추출하는 작업을 수반하는 패턴.자료 구조 data structure:관련된 값들의 집합으로, 종종 리스트, 딕셔너리, 튜플 등으로 구성됩니다.(자료 구조의) 형태 shape:자료 구조의 형, 크기, 조성(composition)의 요약.

연습

[연습 12.3.]

문자열을 받아들여서 빈도가 감소하는 순으로 글자들을 인쇄하는 함수 most_frequent를 작성하세요. 여러 종류의 언어에서 텍스트 샘플을 찾아서 언어별로 글자 빈도가 어떻게 달라지는지 보세요. 여러분의 결과를 http://en.wikipedia.org/wiki/Letter_frequencies 에 있는 표와 비교해 보세요. 답: http://thinkpython.com/code/most_frequent.py.

[연습 12.4.] [anagrams]

다시 보는 애너그램!

  1. 파일에서 단어 목록을 읽어서 ([wordlist] 절을 보세요) 애너그램을 이루는 모든 단어집합을 인쇄하는 프로그램을 작성하세요.

    여기에 출력 예가 있습니다:

    >>> t = tuple()
    >>> print t
    ()
    
    5

    힌트: 글자 집합을 그 글자들로 구성되는 단어의 리스트로 대응하는 딕셔너리를 만들고 싶을 겁니다. 질문은 이겁니다, 어떻게 키로 사용될 수 있도록 글자의 집합을 표현할 수 있을까요?

  2. 가장 큰 애너그램 집합을 먼저 인쇄하고, 두 번째로 큰 집합을 그 다음에 인쇄하고, 그 나머지도 비슷한 방식으로 인쇄되도록 앞의 프로그램을 수정하세요.

  3. 스크래블(Scrabble)에서 “빙고(bingo)” 는 여러분 몫의 일곱 철자 모두와 보드의 한 글자를 써서 여덟 글자 단어를 만든 경우 입니다. 어떤 여덟 글자가 가장 확률이 높은 빙고를 구성할까요? 힌트: 7개 있습니다.

    답: http://thinkpython.com/code/anagram_sets.py.

[연습 12.5.]

두 글자를 교환해서 한 단어를 다른 단어로 만들 수 있다면, 두 단어가 “자위 전환 쌍(metathesis pair)”을 이룬다고 합니다; 예를 들어, “converse” 와 “conserve” 같은 경우입니다. 앞의 문제에서 나온 딕셔너리에서 모든 자위 전환 쌍을 찾아내는 프로그램을 작성하세요. 힌트: 단어의 모든 쌍을 조사하지 말고, 모든 가능한 교환을 검사하지도 마세요. 답: http://thinkpython.com/code/metathesis.py. 출처: 이 연습은 http://puzzlers.org의 예에서 영감을 얻었습니다.

[연습 12.6.]

여기 또 하나의 Car Talk 퍼즐(http://www.cartalk.com/content/puzzlers)이 있습니다:

한 번에 한 글자씩 지워나가도, 계속 올바른 영어 단어로 남아있는, 가장 긴 영어 단어는 무엇일까요?

자, 글자는 양 끝과 중간 어디서든 지워질 수 있습니다만, 글자들을 재배열 할 수는 없습니다. 글자를 지울 때마다, 다른 영어 단어와 만나게 됩니다. 여러분이 그렇게 한다면, 결국 한 글자에 도달하게 되는데, 이 또한 사전에서 찾을 수 있는 영어 단어가 됩니다. 저는 가장 긴 단어가 무엇인지 알고 싶습니다, 몇 글자 단어인가요?

약간 평범한 예를 보여주겠습니다: Sprite. Ok? sprite 로 시작합니다, 한 글자를 지워야 하는데, 단어의 중간에서, r 을 때어내면 spite 가 남고, 끝의 e 를 지우면, spit 가 되며, s 를 지워서 pit 얻은 다음, it 을 거쳐 I 가 됩니다.