세트
- -
Unit 26. 세트 사용하기
파이썬은 집합을 표현하는 세트(set)라는 자료형을 제공합니다. 집합을 영어로 하면 세트인데 수학에서 배우는 그 집합이 맞습니다. 따라서 세트는 합집합, 교집합, 차집합 등의 연산이 가능합니다.
이번 유닛에서는 세트를 만드는 방법과 세트 메서드 사용 방법을 알아보겠습니다.
26.1 세트 만들기
세트는 { }(중괄호) 안에 값을 저장하며 각 값은 , (콤마)로 구분해줍니다.
- 세트 = {값1, 값2, 값3}
간단하게 과일이 들어있는 세트를 만들어보겠습니다.
>>> fruits = {'strawberry', 'grape', 'orange', 'pineapple', 'cherry'}
>>> fruits
{'pineapple', 'orange', 'grape', 'strawberry', 'cherry'}
SyntaxError: invalid syntax: { }의 짝이 맞지 않을 때, 문자열의 ' ' 짝이 맞지 않을 때, 각 요소를 구분할 때 ,를 넣지 않아서 발생하는 구문 에러입니다. { }, ' ' 짝이 맞는지, ,를 빠뜨리지 않았는지 확인해주세요.
세트는 요소의 순서가 정해져 있지 않습니다(unordered). 따라서 세트를 출력해보면 매번 요소의 순서가 다르게 나옵니다.
또한, 세트에 들어가는 요소는 중복될 수 없습니다. 다음과 같이 세트에 'orange'를 두 개 넣어도 실제로는 한 개만 들어갑니다.
>>> fruits = {'orange', 'orange', 'cherry'}
>>> fruits
{'cherry', 'orange'}
특히 세트는 리스트, 튜플, 딕셔너리와는 달리 [ ](대괄호)로 특정 요소만 출력할 수는 없습니다.
>>> fruits = {'strawberry', 'grape', 'orange', 'pineapple', 'cherry'}
>>> print(fruits[0])
Traceback (most recent call last):
File "<pyshell#42>", line 1, in <module>
print(fruits[0])
TypeError: 'set' object does not support indexing
>>> fruits['strawberry']
Traceback (most recent call last):
File "<pyshell#43>", line 1, in <module>
fruits['strawberry']
TypeError: 'set' object is not subscriptable
26.1.1 세트에 특정 값이 있는지 확인하기
그럼 세트에 특정 값이 있는지 확인하려면 어떻게 해야 할까요? 지금까지 리스트, 튜플, 딕셔너리에 사용했던 in 연산자를 사용하면 됩니다.
- 값 in 세트
>>> fruits = {'strawberry', 'grape', 'orange', 'pineapple', 'cherry'}
>>> 'orange' in fruits
True
>>> 'peach' in fruits
False
이처럼 세트에 특정 값이 있으면 True, 없으면 False가 나옵니다. 세트 fruits에 'orange'가 있으므로 True, 'peach'가 없으므로 False가 나왔습니다.
반대로 in 앞에 not을 붙이면 특정 값이 없는지 확인합니다.
- 값 not in 세트
>>> 'peach' not in fruits
True
>>> 'orange' not in fruits
False
이렇게 not in은 특정 값이 없으면 True, 있으면 False가 나옵니다.
26.1.2 set를 사용하여 세트 만들기
이번에는 set를 사용하여 세트를 만들어보겠습니다.
- set(반복 가능한 객체)
set에는 반복 가능한 객체(iterable)를 넣습니다 여기서는 간단하게 문자열과 range로 세트를 만들어보겠습니다.
set('apple')과 같이 영문 문자열을 세트로 만들면 'apple'에서 유일한 문자인 'a', 'p', 'l', 'e'만 세트로 만들어집니다. 즉, 중복된 문자는 포함되지 않습니다.
>>> a = set('apple') # 유일한 문자만 세트로 만듦
>>> a
{'e', 'l', 'a', 'p'}
그리고 set(range(5))와 같이 숫자를 만들어내는 range를 사용하면 0부터 4까지 숫자를 가진 세트를 만들 수 있습니다.
>>> b = set(range(5))
>>> b
{0, 1, 2, 3, 4}
빈 세트는 c = set()과 같이 set에 아무것도 지정하지 않으면 됩니다.
>>> c = set()
>>> c
set()
단, 세트가 { }를 사용한다고 해서 c = {}와 같이 만들면 빈 딕셔너리가 만들어지므로 주의해야 합니다. 다음과 같이 type을 사용하면 자료형의 종류를 알 수 있습니다.
- type(객체)
>>> c = {}
>>> type(c)
<class 'dict'>
>>> c = set()
>>> type(c)
<class 'set'>
set을 사용하여 한글 문자열을 세트로 만들면 다음과 같이 음절 단위로 세트가 만들어집니다.
>>> set('안녕하세요')
{'녕', '요', '안', '세', '하'}
세트는 리스트, 딕셔너리와 달리 세트 안에 세트를 넣을 수 없습니다.
>>> a = {{1, 2}, {3, 4}}
Traceback (most recent call last):
File "<pyshell#3>", line 1, in <module>
a = {{1, 2}, {3, 4}}
TypeError: unhashable type: 'set'
파이썬은 내용을 변경할 수 없는 세트도 제공합니다.
프로즌 세트 = frozenset(반복 가능한 객체)
>>> a = frozenset(range(10))
>>> a
frozenset({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
이름 그대로 얼어 있는(frozen) 세트입니다. frozenset는 뒤에서 설명할 집합 연산과 메서드에서 요소를 추가하거나 삭제하는 연산, 메서드는 사용할 수 없습니다. 즉, 다음과 같이 frozenset의 요소를 변경하려고 하면 에러가 발생합니다.
>>> a = frozenset(range(10))
>>> a |= 10
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
a |= 10
TypeError: unsupported operand type(s) for |=: 'frozenset' and 'int'
>>> a.update({10})
Traceback (most recent call last):
File "<pyshell#5>", line 1, in <module>
a.update({10})
AttributeError: 'frozenset' object has no attribute 'update'
그런데 요소를 변경할 수 없는 frozenset는 왜 사용할까요? frozenset는 세트 안에 세트를 넣고 싶을 때 사용합니다. 다음과 같이 frozenset는 frozenset를 중첩해서 넣을 수 있습니다. 단, frozenset만 넣을 수 있고, 일반 set는 넣을 수 없습니다.
>>> frozenset({frozenset({1, 2}), frozenset({3, 4})})
frozenset({frozenset({1, 2}), frozenset({3, 4})})
이제 세트에서 집합 연산과 이에 대응하는 메서드를 사용해보겠습니다. 집합 연산은 파이썬의 산술 연산자와 논리 연산자를 활용합니다.
| 연산자는 합집합(union)을 구하며 OR 연산자 |를 사용합니다. set.union 메서드와 동작이 같습니다. 다음은 세트 {1, 2, 3, 4}와 {3, 4, 5, 6}을 모두 포함하므로 {1, 2, 3, 4, 5, 6}이 나옵니다.
- 세트1 | 세트2
- set.union(세트1, 세트2)
>>> a = {1, 2, 3, 4}
>>> b = {3, 4, 5, 6}
>>> a | b
{1, 2, 3, 4, 5, 6}
>>> set.union(a, b)
{1, 2, 3, 4, 5, 6}
▼ 그림 26-1 세트의 합집합 연산

& 연산자는 교집합(intersection)을 구하며 AND 연산자 &를 사용합니다. set.intersection 메서드와 동작이 같습니다. 다음은 세트 {1, 2, 3, 4}와 {3, 4, 5, 6} 중에서 겹치는 부분을 구하므로 {3, 4}가 나옵니다.
- 세트1 & 세트2
- set.intersection(세트1, 세트2)
>>> a & b
{3, 4}
>>> set.intersection(a, b)
{3, 4}
▼ 그림 26-2 세트의 교집합 연산

- 연산자는 차집합(difference)을 구하며 뺄셈 연산자 -를 사용합니다. set.difference 메서드와 동작이 같습니다. 다음은 {1, 2, 3, 4}에서 {3, 4, 5, 6}과 겹치는 3과 4를 뺐으므로 {1, 2}가 나옵니다.
- 세트1 - 세트2
- set.difference(세트1, 세트2)
>>> a - b
{1, 2}
>>> set.difference(a, b)
{1, 2}
▼ 그림 26-3 세트의 차집합 연산

^ 연산자는 대칭 차집합(symmetric difference)을 구하며 XOR 연산자 ^를 사용합니다. set.symmetric_difference 메서드와 동작이 같습니다.
대칭 차집합은 XOR 연산자의 특성을 그대로 따르는데 XOR은 서로 다르면 참입니다. 따라서 집합에서는 두 집합 중 겹치지 않는 요소만 포함합니다. 다음은 세트 {1, 2, 3, 4}와 {3, 4, 5, 6} 중에서 같은 값 3과 4를 제외한 다른 모든 요소를 구하므로 {1, 2, 5, 6}이 나옵니다.
- 세트1 ^ 세트2
- set.symmetric_difference(세트1, 세트2)
>>> a ^ b
{1, 2, 5, 6}
>>> set.symmetric_difference(a, b)
{1, 2, 5, 6}
▼ 그림 26-4 세트의 대칭 차집합 연산

26.2.1 집합 연산 후 할당 연산자 사용하기
이번에는 세트 자료형에 집합 연산 후 할당 연산자와 이에 대응하는 메서드를 사용해보겠습니다. 세트 자료형에 |, &, -, ^ 연산자와 할당 연산자 =을 함께 사용하면 집합 연산의 결과를 변수에 다시 저장(할당)합니다.
|=은 현재 세트에 다른 세트를 더하며 update 메서드와 같습니다. 다음은 세트 {1, 2, 3, 4}에 {5}를 더하므로 {1, 2, 3, 4, 5}가 나옵니다.
- 세트1 |= 세트2
- 세트1.update(세트2)
>>> a = {1, 2, 3, 4}
>>> a |= {5}
>>> a
{1, 2, 3, 4, 5}
>>> a = {1, 2, 3, 4}
>>> a.update({5})
>>> a
{1, 2, 3, 4, 5}
&=은 현재 세트와 다른 세트 중에서 겹치는 요소만 현재 세트에 저장하며 intersection_update 메서드와 같습니다. 다음은 세트 {1, 2, 3, 4}와 {0, 1, 2, 3, 4} 중에서 겹치는 요소만 a에 저장하므로 {1, 2, 3, 4}가 나옵니다.
- 세트1 &= 세트2
- 세트1.intersection_update(세트2)
>>> a = {1, 2, 3, 4}
>>> a &= {0, 1, 2, 3, 4}
>>> a
{1, 2, 3, 4}
>>> a = {1, 2, 3, 4}
>>> a.intersection_update({0, 1, 2, 3, 4})
>>> a
{1, 2, 3, 4}
-=은 현재 세트에서 다른 세트를 빼며 difference_update 메서드와 같습니다. 다음은 세트 {1, 2, 3, 4}에서 {3}을 빼므로 {1, 2, 4}가 나옵니다.
- 세트1 -= 세트2
- 세트1.difference_update(세트2)
>>> a = {1, 2, 3, 4}
>>> a -= {3}
>>> a
{1, 2, 4}
>>> a = {1, 2, 3, 4}
>>> a.difference_update({3})
>>> a
{1, 2, 4}
^=은 현재 세트와 다른 세트 중에서 겹치지 않는 요소만 현재 세트에 저장하며 symmetric_difference_update 메서드와 같습니다. 다음은 세트 {1, 2, 3, 4}와 {3, 4, 5, 6} 중에서 겹치지 않는 요소만 a에 저장하므로 {1, 3}이 나옵니다.
- 세트1 ^= 세트2
- 세트1.symmetric_difference_update(세트2)
>>> a = {1, 2, 3, 4}
>>> a ^= {3, 4, 5, 6}
>>> a
{1, 2, 5, 6}
>>> a = {1, 2, 3, 4}
>>> a.symmetric_difference_update({3, 4, 5, 6})
>>> a
{1, 2, 5, 6}
26.2.2 부분 집합과 상위 집합 확인하기
세트는 부분집합, 진부분 집합, 같이 속하는 관계를 표현할 수도 있습니다. 현재 세트가 다른 세트의 (진) 부분집합 또는 (진) 상위 집합인지 확인할 때는 세트 자료형에 부등호와 등호 사용합니다.
<=은 현재 세트가 다른 세트의 부분집합(subset)인지 확인하며 issubset 메서드와 같습니다. 다음은 세트 {1, 2, 3, 4}가 {1, 2, 3, 4}의 부분집합이므로 참입니다(등호가 있으므로 두 세트가 같을 때도 참입니다).
- 현재세트 <= 다른세트
- 현재세트.issubset(다른세트)
>>> a = {1, 2, 3, 4}
>>> a <= {1, 2, 3, 4}
True
>>> a.issubset({1, 2, 3, 4, 5})
True
▼ 그림 26-5 세트가 부분집합인지 확인

<은 현재 세트가 다른 세트의 진부분 집합(proper subset)인지 확인하며 메서드는 없습니다. 다음은 세트 {1, 2, 3, 4}가 {1, 2, 3, 4, 5}의 진부분 집합이므로 참입니다. 즉, 부분집합이지만 같지는 않을 때 참입니다.
- 현재세트 < 다른세트
>>> a = {1, 2, 3, 4}
>>> a < {1, 2, 3, 4, 5}
True
▼ 그림 26-6 세트가 진부분 집합인지 확인

>=은 현재 세트가 다른 세트의 상위 집합(superset)인지 확인하며 issuperset 메서드와 같습니다. 다음은 세트 {1, 2, 3, 4}가 {1, 2, 3, 4}의 상위 집합이므로 참입니다(등호가 있으므로 두 세트가 같을 때도 참입니다).
- 현재세트 >= 다른세트
- 현재세트.issuperset(다른세트)
>>> a = {1, 2, 3, 4}
>>> a >= {1, 2, 3, 4}
True
>>> a.issuperset({1, 2, 3, 4})
True
▼ 그림 26-7 세트가 상위 집합인지 확인

>은 현재 세트가 다른 세트의 진상 위집합(proper superset)인지 확인하며 메서드는 없습니다. 다음은 세트 {1, 2, 3, 4}가 {1, 2, 3}의 진상 위 집합이므로 참입니다. 즉, 상위 집합이지만 같지는 않을 때 참입니다.
- 현재세트 > 다른세트
>>> a = {1, 2, 3, 4}
>>> a > {1, 2, 3}
True
▼ 그림 26-8 세트가 진상 위 집합인지 확인

26.2.3 세트가 같은지 다른지 확인하기
세트는 == 연산자를 사용하여 서로 같은지 확인할 수 있습니다.
>>> a = {1, 2, 3, 4}
>>> a == {1, 2, 3, 4}
True
>>> a == {4, 2, 1, 3}
True
세트는 요소의 순서가 정해져 있지 않으므로 ==로 비교했을 때 각 요소만 같으면 참입니다.
!= 연산자는 세트가 다른지 확인합니다.
>>> a = {1, 2, 3, 4}
>>> a != {1, 2, 3}
True
26.2.4 세트가 겹치지 않는지 확인하기
disjoint는 현재 세트가 다른 세트와 겹치지 않는지 확인합니다. 겹치는 요소가 없으면 True, 있으면 False입니다.
- 현재세트.isdisjoint(다른세트)
>>> a = {1, 2, 3, 4}
>>> a.isdisjoint({5, 6, 7, 8}) # 겹치는 요소가 없음
True
>>> a.isdisjoint({3, 4, 5, 6}) # a와 3, 4가 겹침
False
26.3.1 세트에 요소 추가하기
add(요소)는 세트에 요소를 추가합니다.
>>> a = {1, 2, 3, 4}
>>> a.add(5)
>>> a
{1, 2, 3, 4, 5}
26.3.2 세트에서 특정 요소를 삭제하기
remove(요소)는 세트에서 특정 요소를 삭제하고 요소가 없으면 에러를 발생시킵니다.
>>> a.remove(3)
>>> a
{1, 2, 4, 5}
discard(요소)는 세트에서 특정 요소를 삭제하고 요소가 없으면 그냥 넘어갑니다. 다음은 세트 a에 2가 있으므로 2를 삭제하고, 3은 없으므로 그냥 넘어갑니다.
>>> a.discard(2)
>>> a
{1, 4, 5}
>>> a.discard(3)
>>> a
{1, 4, 5}
26.3.3 세트에서 임의의 요소 삭제하기
pop()은 세트에서 임의의 요소를 삭제하고 해당 요소를 반환합니다. 만약 요소가 없으면 에러를 발생시킵니다.
>>> a = {1, 2, 3, 4}
>>> a.pop()
1
>>> a
{2, 3, 4}
26.3.4 세트의 모든 요소를 삭제하기
clear()는 세트에서 모든 요소를 삭제합니다.
>>> a.clear()
>>> a
set()
26.3.5 세트의 요소 개수 구하기
지금까지 리스트, 튜플, 문자열, range, 딕셔너리의 요소 개수를 구할 때 len 함수를 사용했죠?
마찬가지로 len(세트)는 세트의 요소 개수(길이)를 구합니다.
>>> a = {1, 2, 3, 4}
>>> len(a)
4
세트도 리스트, 딕셔너리처럼 할당과 복사의 차이점이 있습니다. 먼저 세트를 만든 뒤 다른 변수에 할당합니다.
>>> a = {1, 2, 3, 4}
>>> b = a
b = a와 같이 세트를 다른 변수에 할당하면 세트는 두 개가 될 것 같지만 실제로는 세트가 한 개입니다.
a와 b를 is 연산자로 비교해보면 True가 나옵니다. 즉, 변수 이름만 다를 뿐 세트 a와 b는 같은 객체입니다.
>>> a is b
True
a와 b는 같으므로 b에 요소를 추가하면 세트 a와 b에 모두 반영됩니다.
>>> b.add(5)
>>> a
{1, 2, 3, 4, 5}
>>> b
{1, 2, 3, 4, 5}
세트 a와 b를 완전히 두 개로 만들려면 copy 메서드로 모든 요소를 복사해야 합니다.
>>> a = {1, 2, 3, 4}
>>> b = a.copy()
이제 a와 b를 is 연산자로 비교해보면 False가 나옵니다. 즉, 두 세트는 다른 객체입니다. 그러나 복사한 요소는 같으므로 ==로 비교하면 True가 나옵니다.
>>> a is b
False
>>> a == b
True
세트 a와 b는 별개이므로 한쪽의 값을 변경해도 다른 세트에 영향을 미치지 않습니다. 다음과 같이 세트 b에 요소를 추가하면 세트 a는 그대로이고 세트 b만 바뀝니다.
>>> a = {1, 2, 3, 4}
>>> b = a.copy()
>>> b.add(5)
>>> a
{1, 2, 3, 4}
>>> b
{1, 2, 3, 4, 5}
이번에는 세트와 for 반복문을 사용하여 요소를 출력해보겠습니다. 간단하게 for in 뒤에 세트만 지정하면 됩니다.
for 변수 in 세트:
반복할 코드
다음은 for로 세트 a의 요소를 출력합니다.
>>> a = {1, 2, 3, 4}
>>> for i in a:
... print(i)
...
1
2
3
4
for i in a:는 세트 a에서 요소를 꺼내서 i에 저장하고, 꺼낼 때마다 코드를 반복합니다. 따라서 print로 i를 출력하면 요소를 모두 출력할 수 있습니다. 단, 세트의 요소는 순서가 없으므로 출력할 때마다 순서가 달라집니다(숫자로만 이루어진 세트는 순서대로 출력됨).
물론 in 다음에 세트를 직접 지정해도 상관없습니다.
for i in {1, 2, 3, 4}:
print(i)
세트는 for 반복문과 if 조건문을 사용하여 세트를 생성할 수 있습니다. 다음과 같이 세트 안에 식과 for 반복문을 지정하면 됩니다.
- {식 for 변수 in 반복가능한객체}
- set(식 for 변수 in 반복가능한객체)
>>> a = {i for i in 'apple'}
>>> a
{'l', 'p', 'e', 'a'}
{ } 또는 set() 안에 식, for, 변수, in, 반복 가능한 객체를 지정하여 세트를 생성합니다. 여기서는 반복 가능한 객체로 문자열 'apple'을 지정했습니다.
다음과 같이 문자열 'apple'에서 유일한 문자인 'a', 'p', 'l', 'e'만 변수 i에 꺼내고 최종적으로 i로 세트를 만듭니다. 즉, 문자열에서 중복된 문자는 세트에 포함되지 않습니다.
▼ 그림 26-9 세트 표현식의 동작 순서

26.6.1 세트 표현식에 if 조건문 사용하기
이번에는 세트 표현식에서 if 조건문을 사용해보겠습니다. 다음과 같이 if 조건문은 for 반복문 뒤에 지정합니다.
- {식 for 변수 in 세트 if 조건식}
- set(식 for 변수 in 세트 if 조건식)
>>> a = {i for i in 'pineapple' if i not in 'apl'}
>>> a
{'e', 'i', 'n'}
{i for i in 'pineapple' if i not in 'apl'}은 문자열 'pineapple'에서 'a', 'p', 'l'을 제외한 문자들로 세트를 생성합니다. 즉, 다음과 같이 for 반복문 뒤에 if 조건문을 지정하면 if 조건문에서 특정 요소를 제외한 뒤 세트를 생성합니다.
▼ 그림 26-10 세트 표현식에서 if 조건문 사용하기

if i not in 'apl'은 {i for i in 'pineapple' if i != 'a' and i != 'p' and i != 'l'}과 같이 문자를 하나씩 비교하고 and로 연결하는 if 조건문과 같습니다.
지금까지 세트의 사용 방법에 대해 알아보았습니다. 아무래도 세트는 리스트와 딕셔너리보다 사용 빈도가 낮습니다. 여기서는 세트가 수학의 집합을 자료형으로 만든 것이라는 정도만 알아두면 됩니다.
참고자료 : https://www.gilbut.co.kr/book/view?bookcode=BN002245
'개인공부 > Python' 카테고리의 다른 글
함수 사용하기 (0) | 2022.07.28 |
---|---|
파일 사용하기 (0) | 2022.07.28 |
python 심사문제 UNIT(25 ~ 35) (0) | 2022.07.28 |
딕셔너리 응용하기 (0) | 2022.07.28 |
리스트와 튜플 응용하기 (0) | 2022.07.28 |
소중한 공감 감사합니다