본문 바로가기

자료구조

[Python/파이썬] 세트 자료형

이번 글에서는 파이썬에서 사용 가능한 집합(set) 자료형에 대해 배워보겠습니다! 

1. 집합(Set) 자료형

집합 자료형은 말 그대로 집합을 표현하고 2개 이상의 집합들의 연산을 위해 사용합니다. 집합 자료형의 특징으로는 중복된 데이터가 없으며, 데이터의 순서가 보장되지 않기 때문에(인덱싱 방법 사용 불가!, TypeError) 주의해야 합니다.

mySet = {1,2,3,3,4,5} 
print(mySet) # {1,2,3,4,5} <- 중복된 3 값이 사라진것을 확인!!

만약 set 자료형에 저장된 값을 인덱싱 방법으로 접근하고 싶다면 list() 함수나 tuple() 함수를 사용하여 형변환 해준 뒤 사용하면 됩니다. 

mySet = {1,2,3,3,4,5} 

myList = list(mySet)
myTuple = tuple(mySet)

print(myList[0])  # 1
print(myTuple[1]) # 2
print(mySet[2])   # TypeError

1.1. 집합(Set) 생성하기

집합 자료형을 생성하기 위해서는 앞의 딕셔너리와 같은 { } 중괄호를 사용합니다. 또한, 리스트 혹은 문자열처럼 반복가능한(iterable) 자료형에 set( )을 사용하여 집합 자료형으로 형변환 시킬 수 있습니다. 이때, 위와 마찬가지로 데이터 자료형 안의 중복된 데이터는 삭제됩니다.

mySet = {1,2,3,3,4,5} 
tmpSet = set('apple')

print(mySet) # {1,2,3,4,5}
print(tmpSet) # {'p', 'e', 'a', 'l'}

1.2. 집합(Set) 조작하기

먼저 집합 자료형에서 사용 가능한 간단한 함수들을 배워보겠습니다. 첫 번째로, add() 함수를 사용하면 집합 자료형 내에 데이터를 추가할 수 있습니다. 다만, 추가할 데이터는 해쉬 타입으로 형 변환이 가능한 숫자, 문자열, 튜플 등만 가능합니다. 또한, 데이터가 이미 집합 자료형 내에 존재할 경우, 데이터가 추가로 들어가지는 않습니다. 

mySet = {1,2,3,3,4,5} 

# 문자열 추가 가능
mySet.add('3')
print(mySet) # {1, 2, 3, 4, 5, '3'}

# 튜플 타입도 추가 가능
mySet.add((7,8))
print(mySet) # {1, 2, 3, 4, 5, '3', (7, 8)}

# 존재하는 데이터 추가 시 변함 없음!
mySet.add(1)
print(mySet) # {1, 2, 3, 4, 5, '3', (7, 8)}

# 리스트 데이터는 추가 x
mySet.add([2]) # TypeError -> unhashable type: list

 

다음은 집합 자료형 내에서 데이터를 삭제하는 방법입니다. 먼저, 리스트 타입과 마찬가지로, remove() 함수 안에 집합 자료형 내에 존재하는 데이터를 넣을 경우 데이터를 삭제할 수 있습니다. 만약, remove() 함수를 통해 삭제하려는 데이터가 집합 자료형 내에 없는 경우 KeyError를 볼 수 있습니다.   

mySet = {1,2,3,3,4,5} 

a = mySet.remove(3)
print(mySet) # {1,2,4,5}
print(a) # None

mySet.remove(6) # KeyError

 

discard()함수는 remove() 함수처럼 집합 자료형 내에 존재하는 데이터를 넣을 경우 데이터를 삭제할 수 있습니다. 또한, 삭제하려는 데이터가 집합 자료형 내에 없을 경우에도 Error를 반환하지 않습니다

mySet = {1,2,3,3,4,5} 

a = mySet.discard(3)
print(mySet) # {1,2,4,5}
print(a) # None

mySet.discard(6) # 삭제하려는 값이 자료형 내에 없어도 사용 가능
print(mySet) # {1,2,4,5}

 

앞과 마찬가지로, 집합 자료형은 데이터의 순서가 정해져 있지 않습니다. 그렇기 때문에 pop() 함수를 사용할 경우 상황에 따라 맨 앞의 데이터가 삭제되는 것을 볼 수 있습니다. 

# 첫 번째 시도
tmpSet = set('apple')
print(tmpSet) # {'p', 'a', 'e', 'l'}

a = tmpSet.pop()
print(a) # 'p'
print(tmpSet) # {'a', 'e', 'l'}

# 두 번째 시도
tmpSet = set('apple')
print(tmpSet) # {'a', 'e', 'l', 'p'}

a = tmpSet.pop()
print(a) # 'a' <- 앞과는 결과가 다르게 나옴
print(tmpSet) # {'e', 'l', 'p'}

 

clear() 함수의 경우 집합 데이터를 초기화할 때 쓰입니다. 함수 내에서 집합 데이터를 초기화만 하기 때문에 따로 return 받는 값은 없습니다. 

mySet = {1,2,3,3,4,5} 
a = mySet.clear()
print(mySet) # set()
print(a) # None

1.3. 집합(Set) 타입의 연산

먼저 합집합 하는 방법입니다. 연산자로는 '|=' 를 사용하는 방법이 있으며, union() 함수와 update()를 통해 두 개의 집합(혹은 반복 가능한 데이터)을 합집합 한 후 return 받는 방법이 있습니다. union() 함수의 경우 return 받는 값을 통해 합집합 된 값을 받아 볼 수 있으며, update의 경우 return 값 없이 기존 리스트가 합집합 된 것을 볼 수 있습니다.

mySet = {1,2,3,4,5}
tmpSet = {4,5,6}

# '|=' 연산자 사용
mySet |= tmpSet
print(mySet) # {1, 2, 3, 4, 5, 6}

# union() 함수 사용
result= mySet.union([6,7])
print(result) # {1, 2, 3, 4, 5, 6, 7}
print(mySet) # {1, 2, 3, 4, 5, 6}

# update() 함수 사용
result = mySet.update((7,8))
print(result) # None
print(mySet) # {1, 2, 3, 4, 5, 6, 7, 8}

 

다음은 교집합입니다. 연산자로는 '&=' 를 사용하는 방법이 있으며, intersection() 함수와 intersection_update()를 통해 두 개의 집합(혹은 반복 가능한 데이터)을 교집합 한 후 return 받는 방법이 있습니다. intersection() 함수의 경우 return 받는 값을 통해 교집합 된 값을 받아 볼 수 있으며, intersection_update의 경우 return 값 없이 기존 리스트가 교집합 된 것을 볼 수 있습니다.

mySet = {1,2,3,4,5}
tmpSet = {4,5,6}

# &= 연산자 사용
mySet &= tmpSet
print(mySet) # {4, 5}

# intersection() 함수 사용
result = mySet.intersection(tmpSet)
print(result) # {4, 5}

# intersection_update() 함수 사용
result = mySet.intersection_update(tmpSet)
print(mySet) # {4, 5}
print(result) # None

 

다음은 차집합입니다. 연산자로는 '-=' 를 사용하는 방법이 있으며, difference() 함수와 difference_update()를 통해 두 개의 집합(혹은 반복 가능한 데이터)을 차집합 한 후 return 받는 방법이 있습니다. difference()함수의 경우 return 받는 값을 통해 교집합 된 값을 받아 볼 수 있으며, difference_update의 경우 return 값 없이 기존 리스트가 교집합 된 것을 볼 수 있습니다.

mySet = {1,2,3,4,5}
tmpSet = {4,5,6}

# -= 연산자 사용
mySet -= tmpSet
print(mySet) # {1, 2, 3}

# intersection() 함수 사용
result = mySet.difference(tmpSet)
print(result) # {1, 2, 3}

# intersection_update() 함수 사용
result = mySet.difference_update(tmpSet)
print(mySet) # {1, 2, 3}
print(result) # None

 

다음은 대차집합입니다. 대차집합이란 집합 A, B를 합집합 한 후, 공집합을 빼낸 집합으로써, 집합 AB 전체에서 겹치는 부분을 뺀 요소를 포함하는 집합입니다. 연산자로는 '^=' 를 사용하는 방법이 있으며, symmetric_difference() 함수와 symmetric_difference_update()를 통해 두 개의 집합(혹은 반복 가능한 데이터)을 대차집합 한 후 return 받는 방법이 있습니다. symmetric_difference()함수의 경우 return 받는 값을 통해 교집합 된 값을 받아 볼 수 있으며, symmetric_difference_update의 경우 return 값 없이 기존 리스트가 교집합 된 것을 볼 수 있습니다.

mySet = {1,2,3,4,5}
tmpSet = {4,5,6}

# ^= 연산자 사용
mySet -= tmpSet
print(mySet) # {1, 2, 3, 6}

# intersection() 함수 사용
result = mySet.symmetric_difference(tmpSet)
print(result) # {1, 2, 3, 6}

# intersection_update() 함수 사용
result = mySet.symmetric_difference_update(tmpSet)
print(mySet) # {1, 2, 3, 6}
print(result) # None

1.3. 집합(Set) 안에 특정 값 존재 확인

앞의 단원들과 마찬가지로 in 키워드와 not in 키워드를 사용하여 집합 자료형 안에 특정 데이터가 있는지 확인해 볼 수 있습니다. 

mySet = {1,2,3,4,5}

print(1 in mySet) # True
print(2 not in mySet) # False

1.4. 프로즌 세트(frozen set) 자료형

파이썬에서는 데이터 변경이 불가능한 프로즌 세트 자료형을 제공합니다. 프로즌 세트는 튜플 자료형처럼 자료형 내의 데이터가 바뀌지 않으며, 이러한 특성 때문에 세트 안에 세트를 넣고 싶을 때에나, 딕셔너리 타입에서 세트를 키 값으로 사용해야 할 때 쓸 수 있습니다. 

# 프로즌 세트 생성
f_set = frozenset(range(5))
f_set = frozenset([0,1,2,3,4]} # <- iterable 타입으로 적어줘야함

print(f_set) # frozenset({0,1,2,3,4})

# 프로즌 세트로 세트 안에 세트값 넣기
mySet = {f_set,5,6}
print(mySet) # {5, frozenset({0,1,2,3,4}), 6}

# 프로즌 세트를 딕셔너리 키 값으로
myDict = {f_set:'abcdef'}
print(myDict) # {frozenset({0,1,2,3,4}): 'abcdef'}

# 집합 연산자 사용 가능!
result = f_set.difference([3,4]
print(result) # frozenset({0, 1, 2})