세트는 중괄호{} 안에 값을 저장하고 각 값은 콤마 로 구분한다.
간단한 예시로 과일이 들어있는 세트를 만들어보겠다.
>>> fruits = {'strawberry', 'grape', 'orange', 'pineapple', 'cherry'}
>>> fruits
{'pineapple', 'orange', 'grape', 'strawberry', 'cherry'}
세트는 요소의 순서가 정해져 있지 않다. 따라서 세트를 출력할 때 마다 매번 출력되는 요소의 순서가 바뀐다.
또한 세트는 요쇼의 중복이 불가능하다. 다음과 같이 '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
그렇다면 특정 값이 있는지 확인하려면 어떻게할까? 이전에 리스트,튜플,딕셔너리에서도 사용했던 in
을 사용하면 된다.
>>> fruits = {'strawberry', 'grape', 'orange', 'pineapple', 'cherry'}
>>> 'orange' in fruits
True
>>> 'peach' in fruits
False
>>> 'peach' not in fruits
True
>>> 'orange' not in fruits
False
물론 특정 값이 없는지 확인하기 위해선 not in
을 사용하면 된다.
이번에는 set
를 사용하여 세트를 만들어보자.
set
에는 반복 가능한 객체를 넣는다. 반복 가능한 객체는 뒤에서 보자
>>> a = set('apple') # 유일한 문자만 세트로 만듦
>>> a
{'e', 'l', 'a', 'p'}
set('apple')
과 같이 영문 문자열을 세트로 만들면 중복된 문자를 제외하고 세트가 만들어진다.
>>> b = set(range(5))
>>> b
{0, 1, 2, 3, 4}
위와 같이 range
를 통해서도 세트를 만들 수 있다.
>>> c = set()
>>> c
set()
빈 세트를 만들 때는 위와 같이 만들면 된다.
>>> c = {}
>>> type(c)
<class 'dict'>
>>> c = set()
>>> type(c)
<class 'set'>
단, 주의할 점은 세트가 중괄호{}를 사용한다고 하여 c = {}
를 하면 빈 세트가 아니라 빈 딕셔너리가 만들어지므로 주의해야한다. 위와 같이 type
을 사용하면 자료형의 종류를 알 수 있다.
>>> 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'
세트는 리스트나 딕셔너리와 다르게 세트 안에 세트를 넣을 수 없다.
파이썬에서는 내용을 변경할 수 없는 세트인 프로즌 세트도 제공한다.
>>> a = frozenset(range(10))
>>> a
frozenset({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
말 그대로 얼어 있는(frozen) 세트이다. 따라서 요소를 추가하거나 삭제하는 연산, 메서드를 사용할 수 없다.
>>> 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({1, 2}), frozenset({3, 4})})
frozenset({frozenset({1, 2}), frozenset({3, 4})})
이번에는 세트에서 집한 연산과 이에 대응하는 메서드를 사용해보자. 집한 연산은 산술 연산자와 논리 연산자를 사용한다.
|
연산자는 합집합(union)을 구하며 or 연산자 |
를 사용한다. 이는 set.union
메서드와 동작이 같다.
>>> 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 메서드와 동작이 같다.
>>> a & b
{3, 4}
>>> set.intersection(a, b)
{3, 4}
-
연산자는 차집합(difference) 를 구하며 뺄셈 연산자 -
를 사용한다. 이 동작은 set.differnece
와 동일하다.
>>> a - b
{1, 2}
>>> set.difference(a, b)
{1, 2}
^
연산자는 대칭차집합(symmetric difference)를 구하며 XOR 연산자 ^
를 사용한다. 이 동작은 set.symmetric_difference
와 동일하다.
XOR 은 서로 다르면 참이다. 따라서 두 집합의 겹치지 않는 요소만 포함한다.
>>> a ^ b
{1, 2, 5, 6}
>>> set.symmetric_difference(a, b)
{1, 2, 5, 6}
세트 자료형에 |, &, -, ^
연산자와 할당 연산자 =
를 같이 쓰면 집합 연산 결과를 다시 변수에 할당한다.
|=
는 현재 세트에 다른 세트를 더하며 update
메서드와 같다.
>>> 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
메서드와 동일하다.
>>> 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}
-=
는 현재 세트에 다른 세트를 뺴며 set.difference_update
메서드와 동일하다.
>>> a = {1, 2, 3, 4}
>>> a -= {3}
>>> a
{1, 2, 4}
>>> a = {1, 2, 3, 4}
>>> a.difference_update({3})
>>> a
{1, 2, 4}
^=
는 현재 세트와 다른 세트의 겹치지 않는 요소만 현재 세트에 저장되며 set.symmetric_difference_update
와 동일하다.
>>> 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
메서드와 같다. 등호가 있으므로 둘의 집합이 동일해도 참이다.
>>> a = {1, 2, 3, 4}
>>> a <= {1, 2, 3, 4}
True
>>> a.issubset({1, 2, 3, 4, 5})
True
현재 세트가 다른 세트의 상위 집합(superset)인지 확인 issuperset
메서드와 동일.
등호가 있으므로 동일해도 참이다.
>>> a = {1, 2, 3, 4}
>>> a >= {1, 2, 3, 4}
True
>>> a.issuperset({1, 2, 3, 4})
True
현재 시트가 다른 세트의 진상위 집합(proper superset)인지 확인하며 메서드는 없다.
둘 세트가 동일하면 거짓(False)이다.
>>> 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
>>> a = {1, 2, 3, 4}
>>> a.isdisjoint({5, 6, 7, 8}) # 겹치는 요소가 없음
True
>>> a.isdisjoint({3, 4, 5, 6}) # a와 3, 4가 겹침
False
>>> a = {1, 2, 3, 4}
>>> a.add(5)
>>> a
{1, 2, 3, 4, 5}
>>> a.remove(3)
>>> a
{1, 2, 4, 5}
>>> a.discard(2)
>>> a
{1, 4, 5}
>>> a.discard(3)
>>> a
{1, 4, 5}
>>> a = {1, 2, 3, 4}
>>> a.pop()
1
>>> a
{2, 3, 4}
임의의 요소를 삭제하고 해당 요소를 반환한다
>>> a.clear()
>>> a
set()
>>> a = {1, 2, 3, 4}
>>> len(a)
4
세트도 딕셔너리나 리스트 처럼 할당과 복사의 차이점이 있다.
>>> a = {1, 2, 3, 4}
>>> b = a
>>> b.add(5)
>>> a
{1, 2, 3, 4, 5}
>>> b
{1, 2, 3, 4, 5}
>>> a is b
True
a = b
를 하여도 세트 두 개가 생성되는 것이 아니라 하나의 세트에 두 가지 이름이 할당된 것이다.
세트 a 와 b 를 완전히 두 개로 만드려고 하면 copy
메서드를 사용하면 된다.
>>> 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}
세트도 딕셔너리나 리스트 처럼 for 반복문과 if 조건문으로 세트를 생성할 수 있다.
>>> a = {i for i in 'apple'}
>>> a
{'l', 'p', 'e', 'a'}
{ } 또는 set()
안에 식, 변수, in, 반복 가능한 객체를 지정하여 세트를 생성한다. 여기서 반복 가능한 객체로 "apple" 을 지정하였다.
>>> a = {i for i in 'pineapple' if i not in 'apl'}
>>> a
{'e', 'i', 'n'}