day 4 세트 사용하기

7 minute read

세트 응용하기

파이썬은 집합을 표현하는 세트(set)라는 자료형을 제공합니다. 집합을 영어로 하면 세트인데 수학에서 배우는 그 집합이 맞습니다. 따라서 세트는 합집합, 교집합, 차집합 등의 연산이 가능합니다.

세트 사용하기

>>> 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`

특정값 확인

>>> fruits = {'strawberry', 'grape', 'orange', 'pineapple', 'cherry'}
>>> 'orange' in fruits
True
>>> 'peach' in fruits
False

>>> 'peach' not in fruits
True
>>> 'orange' not in fruits
False

set 함수

>>> a = set('apple')    # 유일한 문자만 세트로 만듦
>>> a
{'e', 'l', 'a', 'p'}

>>> b = set(range(5))
>>> b
{0, 1, 2, 3, 4}

>>> c = set()
>>> c
set()

>>> c = {}
>>> type(c)
<class 'dict'>
>>> c = set()
>>> type(c)
<class 'set'>

>>> 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}

& 연산자는 교집합(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}

- 연산자는 차집합(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}
    
  • 세트1 ^ 세트2
  • set.symmetric_difference(세트1, 세트2)
    >>> a ^ b
    {1, 2, 5, 6}
    >>> set.symmetric_difference(a, b)
    {1, 2, 5, 6}
    

집합 연산 후 할당 연산자 사용하기

=은 현재 세트에 다른 세트를 더하며 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}
    

부분 집합과 상위집합 확인하기

<=은 현재 세트가 다른 세트의 부분집합(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
    

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

  • 현재세트 < 다른세트
    >>> a = {1, 2, 3, 4}
    >>> a < {1, 2, 3, 4, 5}
    True
    

>=은 현재 세트가 다른 세트의 상위집합(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

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

  • 현재세트 > 다른세트
    >>> a = {1, 2, 3, 4}
    >>> a > {1, 2, 3}
    True
    

세트가 같은지 다른지 확인하기

세트는 == 연산자를 사용하여 서로 같은지 확인할 수 있습니다.

>>> 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

세트가 겹치지 않는지 확인하기

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

세트 조작하기

add(요소)는 세트에 요소를 추가합니다.

>>> a = {1, 2, 3, 4}
>>> a.add(5)
>>> a
{1, 2, 3, 4, 5}

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}

pop()은 세트에서 임의의 요소를 삭제하고 해당 요소를 반환합니다. 만약 요소가 없으면 에러를 발생시킵니다.

>>> a = {1, 2, 3, 4}
>>> a.pop()
1
>>> a
{2, 3, 4}

clear()는 세트에서 모든 요소를 삭제합니다.

>>> a.clear()
>>> a
set()

지금까지 리스트, 튜플, 문자열, range, 딕셔너리의 요소 개수를 구할 때 len 함수를 사용했죠?

마찬가지로 len(세트)는 세트의 요소 개수(길이)를 구합니다.

>>> a = {1, 2, 3, 4}
>>> len(a)
4

세트의 할당과 복사

세트도 리스트, 딕셔너리처럼 할당과 복사의 차이점이 있습니다. 먼저 세트를 만든 뒤 다른 변수에 할당합니다.

>>> a = {1, 2, 3, 4}
>>> b = a

>>> a is b
True

>>> b.add(5)
>>> a
{1, 2, 3, 4, 5}
>>> b
{1, 2, 3, 4, 5}

세트 a와 b를 완전히 두 개로 만들려면 copy 메서드로 모든 요소를 복사해야 합니다.

이제 a와 b를 is 연산자로 비교해보면 False가 나옵니다. 즉, 두 세트는 다른 객체입니다. 그러나 복사한 요소는 같으므로 ==로 비교하면 True가 나옵니다.

>>> a = {1, 2, 3, 4}
>>> b = a.copy()

>>> a is b
False
>>> a == b
True

>>> a = {1, 2, 3, 4}
>>> b = a.copy()
>>> b.add(5)
>>> a
{1, 2, 3, 4}
>>> b
{1, 2, 3, 4, 5}

출력하기

>>> a = {1, 2, 3, 4}
>>> for i in a:
...     print(i)
...
1
2
3
4
...
...
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'}
    

이번에는 세트 표현식에서 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'}
    

summary

image image image image image

Leave a comment