Wireframe

Python101 : 다른 집합 유형들

지난 글에서 리스트의 특징에 대해서 언급할 때나, 리스트 축약에 대해서 이야기하면서 “연속열”이나 “반복가능”이라는 단어를 사용한 적이 있다. 사실 파이썬에 연속열이나 반복객체 혹은 반복가능객체는 이제는 거의 공식적으로 사용되는 개념이다. 리스트는 이런 “연속열”과 “반복가능”에 공통적으로 속하는 유형의 데이터 타입인 것이다. 이번 글에서는 그럼 리스트 외에 어떤 다른 집합형 데이터 타입이 있는지 알아보고 그 면면을 좀 살펴보기로 하겠다.

값의 타입

일단 값의 타입을 확인하는 방법을 알아보자. 사실 여기서 말하는 타입들은 아주 “대표적인” 파이썬 타입들이며, 사실 파이썬에는 엄청나게 많은 데이터 유형이 존재한다. (사실 어떤 데이터의 타입을 표현하는 값 자체도 Type 이라는 타입을 가진다.) 파이썬에서 특정한 값의 타입을 확인하는 방법으로는 type() 함수가 있다. 이 함수는 인자로 전달된 값의 타입값 (타입을 나나태는 Type 객체의 값)을 리턴한다. 한편, 특정한 객체가 특정한 타입인지를 확인하고 싶다면 type(a) 를 사용하기 보다는 isintance(a, b) 를 사용하면 된다. 이 함수는 객체 a 가 타입 b에 속하는지를 검사하는 함수이다.

튜플

튜플(tuple)은 변경 불가능한 리스트로 생각할 수 있다. 원소들은 순서대로 나열되어 정수 인덱스 및 슬라이싱을 사용하여 참조할 수 있지만, 한 번 생성된 튜플은 원소를 추가/제거/변경하는 것이 허용되지 않는다.

튜플은 변경불가능(immutable)한 집합이다. 따라서 제공되는 메소드 역시 t.index(a), t.count(a) 정도로 단순하다. 또 원소의 추가에 따른 메모리 재할당등의 동작이 필요없기 때문에, 변경될 필요가 없는 배열과 같은 데이터가 필요하다면 리스트보다는 튜플을 사용하는 것이 권장된다. 튜플 그 자체는 변경 불가능하지만, 튜플의 각 원소가 변경 가능하다면 그에 대한 조작은 허용된다.

리스트 축약 문법을 괄호(( ... ))로 둘러싸서 표현하는 것은 튜플을 생성하지 않는다. 대신 이는 제너레이터 객체이다.

집합 (Set)

리스트가 원소들을 순서대로 줄지어 놓은 것에 비해 set는 이 원소들 사이에 딱히 순서를 부여하지는 않는다. 따라서 정수 인덱스를 사용해서 각각의 원소를 참조할 수 없다.

집합은 각각의 원소에 대해 정수 인덱스가 아닌 해시값을 통해 액세스한다. 따라서 특정 원소의 포함 여부를 검사하는 연산(조금 있는 척해서 ‘멤버십 검사’라고 함)이 매우 빠르다. 그런데 이 때 사용되는 해시 연산은 리스트나 사전, 집합과 같은 변경 가능한 객체에는 적용되지 않는다. 따라서 이들 타입의 객체들은 리스트나 튜플의 원소는 될 수 있지만 집합의 원소는 될 수 없다.

집합은 {1, 2, 3} 처럼 중괄호를 사용한 리터럴을 사용하거나, set() 함수에 연속열/반복가능 객체를 전달하여 생성할 수 있다. 중복된 원소는 1개만 저장된다.

참고로 빈 중괄호를 사용하는 것은 빈 집합이 아닌 빈 사전을 생성한다. 빈 집합을 만들려면 set() 함수를 인자 없이 호출하면 된다. (다른 집합 타입들도 마찬가지로 인자를 주지 않고 생성함수를 호출하면 빈 객체가 만들어진다.)

사전

사전은 정수 인덱스 대신 사용자가 정한 별도의 키를 통해서 데이터를 저장하고 찾는 집합으로, 다른 언어에서는 해시맵/해시테이블이라고도 부르는 자료 구조이다. 집합과 마찬가지로 이 때 키로 사용되는 값은 해시가 가능해야 하므로 사전, 리스트, 집합 타입의 객체는 사전의 키가 될 수 없다. (단, 사전의 값으로는 사용될 수 있다.) 메뉴 이름과 가격처럼 짝지을 수 있는 데이터를 저장하고 관리하는데 유용하게 사용된다.

사전은 집합과 같이 중괄호를 사용하여 만든다. 집합과 차이점은 키와 값을 각각 콜론으로 연결하는 것이다. ({'a': 100, 'b': 200}) 그 외에 생성함수 dict()를 사용하여 만들 수 있다.

사전 축약 문법

리스트 축약과 비슷하게 사전 축약 문법을 사용하여 사전을 생성할 수 있다. 키와 값의 연속열을 각각 가지고 있다면, 이 둘을 짝지어서 { key:value for ... } 의 형태로 작성할 수 있다.

# 키, 값의 데이터가 각각 있을 때
keys = ['apple', 'banana', 'cherry', 'orange']
values = (100, 200, 500, 1000)
d = {key: value for (key, value) in zip(keys, values)}
# ==> d = dict(keys, values)

# 사전의 키-값을 서로 바꾼 사전 만들기
d2 = {value: key for (key, value) in d.items()}

사전에서 사용할 수 있는 메소드들을 몇 가지 소개한다.

문자열

문자열도 각각의 낱자 문자가 순서대로 모여있는 집합이라 할 수 있다. 순서가 있기 때문에 특정 글자나 부분열을 인덱스, 슬라이싱을 통해서 액세스할 수 있다. 문자열은 튜플과 같이 변경 불가능하기 때문에 s[3] = 'c' 와 같이 특정 위치의 문자를 변경할 수는 없다. 문자열은 집합의 특성 외에도 문자열 이라는 데이터 타입을 처리하는데 필요한 고유한 메소드가 많이 있는데, 이 부분은 문자열에 대해 다루는 부분에서 보다 자세히 다루도록 하겠다.

연속열

연속열(Sequence)은 어떤 원소들이 정해진 순서대로 줄지어 있는 집합을 말한다. 리스트, 튜플, 문자열과 같은 타입들이 연속열에 속한다. 연속열의 공통된 특징은 그 내부의 각 원소들이 정수로 구분할 수 있는 순서를 갖는다는 점이며, 따라서 다음과 같은 동작을 할 수 있다는 것을 의미한다.

반복 가능

반복 가능한 객체는 파이썬 공식 문서에서 iterable 라는 표기로 종종 사용된다. 연속열처럼 특정한 한 타입이라기 보다는 어떤 공통된 특징을 공유하는 타입들을 묶어서 말한다. 이들은 공통된 특징으로 인해 비슷한 동작을 할 수 있고, 이런 비슷한 동작을 수행하게 하는 함수나 메소드는 보통 이름이 같도록 만들어져 있다.

반복 가능 객체는 이터레이터(iterator, 반복자)를 생성할 수 있는 객체이며, 내부적으로 __iter__() 메소드를 가지고 있다. 이 특징은 어떤 집합에서 각각의 원소에 대해 어떤 작업을 반복할 수 있도록, 순회할 수 있다는 것을 의미한다.

모든 반복 가능 객체가 연속열일수는 없지만, 연속열에서는 인덱스를 1씩 늘려나가면서 모든 원소를 순회할 수 있으므로 모든 연속열은 반복가능하다고 말할 수 있다. 또한 앞으로 많이 보게 될 range(), map(), filter() 함수의 결과나 사전의 .keys(), .values() 메소드의 리턴 값 역시 리스트가 아닌 반복 가능 객체이다.

반복 가능 객체는 연속열이 아니기 때문에 m[3] 과 같은 subscription을 사용할 수 없다. 리스트나 튜플로 만들어도 될 것 같은 값들을 굳이 이런 별도의 유형으로 만드는 것은 불편해 보일 수 있는데, 다 이렇게 하는 이유가 있다. 그럼 그 이유가 무엇인지에 대해서는 다음 시간에 알아보도록 하고 오늘은 이쯤에서 마무리하도록 하겠다.

Exit mobile version