본문 바로가기

파이썬 (python)

[python 13] 데이터 구조 - 튜플, 딕셔너리

 

 

 

이전의 글에서 파이썬에서 제공하는 가장 중요한 데이터구조로서 리스트(List)를 살펴보았다.  리스트 이외에 파이썬은 튜플(Tuple), 딕셔너리(Dictionary), 셋(Set) 등의 데이터 구조를 제공하고 있다. 어쨌거나 모든 데이터 구조는 리스트를 기본으로 하고 있다. 리스트의 기본적 특성을 이해했다면, 공부하는데 전혀 어려움이 없을 것이다. 반대로, 튜플, 딕셔너리, 셋 등을 공부하면서 리스트에 대한 이해를 좀 더 높일 수 있는 기회가 되기도 한다.

 

이번 글에서는튜플과 딕셔너리에 대해서 살펴보도록 하자. (Set 은 다음 기회에...)

 

1. 튜플(Tuple)

 

튜플은 리스트와 동일하다. 단지, 튜플을 구성하는 데이터들을 항(item) 이라고 부르는데, 튜플은 한번 선언되면 튜플을 구성하는 항의 값을 바꿀 수 없다. 이런 특성을 "not mutable" 하다라고 한다. 즉, 리스트는 mutable 한데, 튜플은 immutable 하다. 그외에는 리스트에서 정의된 모든 연산이 튜플에도 그대로 적용된다.

 

튜플은 여러개의 데이터가 콤마( , )로 구분되어 괄호(parentheses, ( )) 로 묶어서 나타낸다. 예를 들어, (1, 2, 3, 4, 5)는 5개의 요소로 이루어진 튜플이다. 리스트는 대괄호( [ ] )로 묶었지만, 튜플은 소괄호( ( ) )로 묶는 차이가 있다. 그외에 몇가지 특징들을 살펴보면 다음과 같다

 

(1) 비어 있는 튜플은 ( ) 로 표시한다.

(2) 아래 (3)번에서 보듯이 튜플의 선언에 괄호가 반드시 필요한 것은 아니다. 그러다보니, 1개의 요소로 구성된 튜플을 정의 할 때에 조심해야 한다.  a, 또는 (a, )로 정의한다. 

(3) 여러개의 항으로 구성된 튜플은 a, b, c 로 표시하거나 (a, b, c)로 표현한다.

(4) tuple( 리스트 객체 ) 함수를 이용해 리스트를 튜플로 변환시킬 수 있다. 

 

아래의 코드를 살펴보자. In[14]에서 3개의 항으로 구성된 튜플 데이터를 a 라는 이름으로 선언하였다. In[15]에서 type( ) 함수를 활용해 변수의 형을 살펴봤더니 역시 "tuple"이라는 답이 나왔고, 실제 In[16]에서 출력해 보니 우리가 기대하던 결과를 보여주고 있다. 

 

 

튜플도 데이터 구조이니 만큼, 각 데이터 항을 접근하는 것이 중요하다. 리스트와 마찬가지로 인덱스를 활용해서 각 데이터 항목에 대한 참조가 가능하다. 

 

 

단, 일단 선언되면 항의 값을 바꾸는 것은 허용되지 않는다. 다시 말해서, 튜플은 mutable 하지 않다.

 

 

그러다 보니, 튜플에 대해 적용할 수 있는 함수의 종류도 리스트에 비해서는 제한적이다. 언더스코어(_)로 시작하는 이름외에는 count와 index 함수 등으로 2개 뿐이다. 이 2개 함수는 리스트에서도 제공되는 함수로서 기능은 동일하다.

 

 

○ count( ) 함수

 

리스트의  count( ) 함수와 동일하다. 튜플이 제공하는 함수 중에 count( ) 함수는 인자와 같은 값인 아이템의 개수를 찾아서 리턴해 주는 함수이다. 예를 들어 아래와 같이 a=(1, 2, 3, 1, 2, 1) 일 때, a.count(1)이라고 하면 a 튜플의 아이템 중에 1의 개수를 리턴해 주는 함수이다. 

 

 

○ index( ) 함수

 

리스트의 index( ) 함수와 동일하다. a가 어떤 튜플이고 b가 어떤 값일 때, a.index(b) 함수는 a 튜플에서 b 값이 처음으로 발견된 인덱스를 돌려준다. 튜플에 b 값이 없는 경우에는 에러가 발생하는 것도 리스트의 경우와 동일하다.

 

 

○ 리스트와 튜플, 언제 어느곳에 사용하나?

 

튜플은 한번 선언되면 데이터를 바꿀 수 없다는 제한이 있다는 것을 제외하면, 리스트와 튜플은 거의 동일한 데이터 구조이다. 실제 프로그램을 개발하다 보면, 어떤 데이터는 한번 선언되면 절대 바뀌지 않는 데이터들이 있다. 이런 데이터들을 리스트를 이용해서 선언해도 되고 튜플을 이용해서 선언해도 결과는 동일하게 만들 수 있다. 하지만, 개발자의 부주의로 말미암아 바뀌어서는 안되는 데이터를 바꿀려고 시도할 수도 있다. 그런 오류를 사전에 방지할 수 있는 수단으로서 튜플이 유용하다. 

 

특히, 함수에서 데이터를 인자로 주고 받을 때 리스트와 튜플 간에 차이가 있는데, 인자로서 시퀀스 타입의 객체를 넘겨주는데 그 데이터가 함수 내에서 변경되어서는 안될 때, 그리고, 함수로 부터 2개 이상의 값이 리턴될 때 튜플이 사용된다. 구체적 내용들은 함수를 다룰 때 자세히 살펴보도록 하자.

 

 

2. 딕셔너리 (Dictionary)

 

딕셔너리 객체는 key : value 페어(pair) 형태의 데이터를 저장하는 용도로 사용된다.

 

예를 들어, 이번에 중간시험을 봤는데, 국어 성적은 90점, 영어는 95점, 수학은 100점이라고 해 보자. 이 데이터를 리스트로 저장한다면 어떨까? 아래에서 3개의 데이터를 리스트에 저장하고 그 데이터들을 이용해서 평균(average) 값을 구해 봤다. 여기서 90점이라는 데이터는 어떻게 구별되고 있는가? a 리스트의 첫번째 데이터로서 a[0] 으로 나타내어지고 있다. a[0] 이란 데이터는 사실 "a 라는 사람의 첫번째 점수" 라는 뜻이다. 첫번째 점수가 국어점수인지 수학점수인지 영어점수인지 나타나지 않는다. 데이터를 다루는 사람이 인지하고 있어야 한다.  첫번째 데이터가 어느 과목의 점수라고? "국어" 과목의 점수라고... 

 

 

a의 0번째 점수는 a[0], a의 1번째 점수는 a[1], a의 2번째 점수는 a[2]로 나타내듯이, a의 국어(kor)점수를 a['kor'], a의 영어(eng) 점수를 a['eng'], a의 수학(math) 점수를 a['math'] 로 표현하면 안될까?

 

이것을 가능하게 만든 것이 딕셔너리 (Dictionary)이다. 위의 예를 다음과 같이 딕셔너리로 정의할 수 있다. 리스트는 중괄호로, 튜플은 괄호로 묶었는데, 딕셔너리는 중괄호( { } ) 로 묶는 차이점이 눈에 먼저 띈다. In[51]에서 보듯이 타입은 dict 로 표시된다. 

 

다른 데이터구조와 마찬가지로 딕셔너리로 여러개의 아이템으로 구성되며, 그 아이템들은 콤마( , )로 구분되어 있고, 전체 아이템들이 중괄호로 묶여져 있다. 여기서 하나의 아이템은 key:value 페어로 만들어진다. 

 

 

딕셔너리 데이터를 좀 더 일목요연하게 보이면 아래와 같다. 위의 경우보다 key:value 페어가 좀 더 명확하게 관찰된다.

 

 

이와 같이 선언된 딕셔너리 데이터 객체에 대해, 아래와 같이 참조와 수정이 가능하다. 코드에서 보다시피, 딕셔너리 객체인 a에 대해 a[key] 형태로 value에 대한 참조와 수정이 가능하다. 

 

 

정수 인덱스 값을 키(key) 값으로 사용할 수 있다. 

 

 

친구의 연락처 정보를 딕셔너리 객체로 만들려면 어떻게 해야 할까? 우선 친구 연락처를 설명하는데 무슨 정보가 필요한지를 결정할 텐데 이런 정보들이 키(key) 값에 해당한다. 친구 연락처 정보에는 이름(name)과 나이(age)가 필요하고 주소(address)와 전화번호(phone)가 필요하다. 

 

a 라고 부를 어떤 친구는 이름이 'Hong', 나이가 24, 주소가 'Cheonan'이고, 전화번호가 '010-1111-2222' 라고 한다면 이 친구의 연락처 정보는 아래와 같이 선언될 수 있을 것이다.

 

 

이 데이터의 참조와 수정에 대해, 아래와 같은 시나리오를 한번 생각해 보자

(1) a 친구의 전화번호를 알고 싶다.


(2) a 친구가 이번에 서울로 이사를 갔다


(3) 새해가 밝아서 나이가 +1 되었다

 

A List of Dictionary objects : 여러개의 딕셔너리를 리스트에 저장한다

 

한친구에 대한 연락처는 딕셔너리로 표현할 수 있는 것을 살펴봤다. 그럼, 여러친구의 연락처가 묶어져 있는 주소록 (address book)은 어떻게 표현하면 좋을까? 우리가 일반적으로 많이 보게 되는 데이터의 형태는 아래와 같은 테이블 (Table) 형태의 데이터이다. 

 

name age address phone
'Hong' 24 'Cheonan' '010-1111-1111'
'Kim' 26 'Seoul' '010-2222-2222'
'Lee' 23 'Ulsan' '010-3333-3333'

 

이렇게 표현된 데이터를 연산하기 위해서는 이 데이터들을 파이썬 안으로 끌어들여야 한다. 첫번째 행(row) 데이터를 보면 name이 'Hong', age가 24, address가 'Cheonan' 이고 phone이 '010-1111-1111' 인 데이터이다. 이런 데이터를 가장 잘 표현할 수 있는 데이터구조는 무엇이겠는가? 그렇다. 딕셔너리이다.

 

그런데, 이런 데이터가 여러개 있다. 여러개 데이터를 저장할 수 있는 데이터 구조로서 가장 좋은 것은 무엇인가? 그렇다. 리스트이다.

 

결국 우리가 보는 가장 흔한(?) 형태의 데이터인 테이블 구조의 데이터는 딕셔너리의 리스트로 표현할 수 있다. 위의 데이터을 파이썬으로 옮겨가 보면 아래와 같다. 

 

 

테이블 형태의 데이터는 특별한 애플리케이션 (예를 들어, 엑셀이나 데이터베이스) 이 없으면 연산할 수 없다. 그나마, 그 애플리케이션이 제공하는 연산의 형태를 벗어나기 어렵다. 즉, 제한적이다. 

 

하지만, 이러한 데이터를 파이썬으로 불러 들인다면, 우리는 이 데이터에 대해 우리가 원하는 모든 연산을 만들어 낼 수 있다. (아직은 아니지만, 곧)

 

○ 딕셔너리 데이터 객체에 대한 기본 연산(함수)

 

딕셔너리 데이터에 대해서 우리가 실행할 수 있는 연산 또는 함수에는 무엇이 있는지 확인해 보자. 

 

 

다른 데이터 구조와 마찬가지로 언더스코어( _ )로 시작하는 것들 외에, 딕셔너리 객체에 대해 우리가 실행할 수 있는 연산들을 살펴보면, clear( ), copy( ), fromkeys( ), get( ), items( ), keys( ), pop( ), popitem( ), setdefault( ), update( ), values( ) 등이 있다. 

 

일단 key:value 페어의 추가가 필요한데 기본함수에는 없다. 이는 단순하게 아래와 같이 실행할 수 있다. 새롭게 'univ'라는 키(key)로 'Namseoul University'라는 값(value)를 추가하고 싶은 경우는 아래의 In[4]와 같이 만들어 주면 된다. 

 

반대로, 딕셔너리의 pop( ) 함수를 이용하여, 불필요한 key:value 페어 값을 삭제할 수 있다. 예를 들어, a 객체의 'univ' 키에 해당하는 key:value 페어 데이터를 삭제한다면  (위의 테이블에서 univ에 해당하는 컬럼이 만들어졌다가 삭제되는 것으로 생각해 볼 수 있음) a.pop('univ') 라고 하면 된다. 아래 코드의 In[17]을 참고하기 바란다.

 

 

참고로, 위의 In[17]이 실행되면서 Out[17]에 'Namseoul University' 라는 값이 출력되었는데, 이것은 파이썬이 코드의 실행결과로 리턴한 값이 있음을 의미한다. 이 경우에, 그 값을 변수로 따로 저장하고 싶다면 b=a.pop('univ') 로 실행하면 된다. 

 

저장된 값을 참조하는 경우에는 딕셔너리객체[key] 를 사용하면 된다. 첨자의 key가 존재하는 경우에는 그 key에 해당하는 value 가 리턴되고 (아래 In[6] 참조), 첨자의 key 가 존재하지 않는 경우에는 에러가 발생한다 (아래의 In[7] 참조)

 

 

위의 연산을 딕셔너리의 get( ) 함수를 이용해서 만들 수 있다. a 객체의 'name' 키에 해당하는 value를 알고 싶다면 a.get('name') 으로 호출하면 된다. In[13]에서는 그렇게 호출하여 리턴된 값을 b 변수에 저장하는 코드이다. 실제 그 값은 In[14]에서 출력되고 있다. 

 

 

위와 동일한 방식으로 value의 수정이 가능하다. 예를 들어, a 딕셔너리 객체의 'phone' 값이 '010-5555-5555' 로 바뀌었다면 아래와 같이 나타내면 된다.

 

 

그외의 연산들에 대해서는 각자가 한번씩 실행해 보면 좋겠다. (모두 소개하려니 너무 길어진다 ㅠ)