점프 투 파이썬 : https://wikidocs.net/book/1
파이썬 기본을 갈고 닦자 : https://wikidocs.net/16031
수학의 집합을 쉽게 사용하기위해 만들어진 자료구조이다. 집합과 같은 특성을 지니는데 다음과 같다.
set()
또는 {}
을 통해 선언할 수 있다. 다만 {}
의 경우 dictionary와 헷갈리므로 {값}
으로 적어주야 한다. 즉 key가 없다.집합은 set()
과 {}
을 통해 선언할 수 있다. 다만 {}
의 경우 dictionary와의 차이를 위해 key : value
가 아닌 value
만 넣어준다.
s1 = {2 , 3 ,4 ,5}
s2 = set({ 2, 3, 4, 5})
print( s1 ) # {2, 3, 4, 5}
print( s2 ) # {2, 3, 4, 5}
set(집합) 내부 원소는 다양한 값을 함께 가질 수 있지만, 가변 객체는 값으로 가질 수 없다. 즉, 가변 객체인dict, list, set 등
는 들어갈 수 없다. 이유는 이들은 집합이 가진 유일성
조건을 해칠 수 있기 때문이다.
s1 = {[1,2,3,4]} # TypeError: unhashable type: 'list'
s2 = {(1,2,3)}
s3 = {"hello", "hi"}
s4 = {{"key" : 2}} # TypeError: unhashable type: 'dict'
다음과 같이 list, dict
가 안되는 모습을 볼 수 있다.
이처럼 불변 객체만 허용하고, 가변 객체를 허용하지 않는 이유는 집합은 중복을 허용하지 않기때문이다. 불변성을 갖춰져야 값으로서 비교를 하고 중복인지 아닌지를 판별할 수 있다. 또한, 집합은 순서도 없다.
s1 = set("hello")
print(s1) # {'l', 'o', 'e', 'h'}
'hello'
의 ll
두 개 중 하나만 살아남고 하나는 사라진 것을 확인할 수 있다. 이 처럼 집합은 중복을 허용하지 않는다.
또한, 순서가 의미가 없기 때문에 인덱싱 또한 지원하지 않는다.
set은 집합 연산을 하기위해 사용하는데, 집합 연산이 자주 사용되는 것은 아니더라도, 알아두면 매우 유용하다.
교집합은 두 집합의 공통된 부분을 떼어내는 것으로 & 또는 intersection()
을 사용하여 찾아낼 수 있다. 마치 and
연산과 유사하다.
s1 = set([1, 2, 3 ,4 ,5 ,6])
s2 = set([4, 5, 6 ,7 ,8 ,9])
print(s1 & s2) # {4, 5, 6}
print(s1.intersection(s2)) # {4, 5, 6}
합집합은 두 집합을 합치는 것이다. 중복된 값은 하나만 나오게 된다. | 또는 union()
을 사용하여 표현할 수 있다. 마치 or
연산과 유사하다.
s1 = set([1, 2, 3 ,4 ,5 ,6])
s2 = set([4, 5, 6 ,7 ,8 ,9])
print(s1 | s2) # {1, 2, 3, 4, 5, 6, 7, 8, 9}
print(s1.union(s2)) # {1, 2, 3, 4, 5, 6, 7, 8, 9}
차집합은 두 집합의 공통된 부분을 제거하고 앞의 집합에 해당하는 원소만 남기는 연산이다. - 또는 difference()
을 통해 사용할 수 있으며, a - b
인 경우에 a와 b의 공통된 부분을 제거한 a 나머지를 돌려준다.
s1 = set([1, 2, 3 ,4 ,5 ,6])
s2 = set([4, 5, 6 ,7 ,8 ,9])
print(s1 - s2) # {1, 2, 3}
print(s2.difference(s1)) # {8, 9, 7}
먼저 합집합을 시켜놓고 교집합을 빼는 것이다. 차집합이 연산 앞에 있는 집합의 나머지라면 이건 둘의 나머지가 되는 것이다. ^ 또는 symmetric_difference
을 사용하면 된다.
s1 = set([1, 2, 3 ,4 ,5 ,6])
s2 = set([4, 5, 6 ,7 ,8 ,9])
print(s1 ^ s2) # {1, 2, 3, 7, 8, 9}
print(s2.symmetric_difference(s1)) # {1, 2, 3, 7, 8, 9}
참고로
|, &, - ,^
은=
과 조합하여 사용이 가능하다. 즉,a |= b, a &= b, a ^= b, a -= b
등이 가능하다.
src.issubset(target)
으로 할 src가 target의 부분집합인지 확인할 수 있다.
s1 = set([1, 2, 3 ,4 ,5 ,6])
s2 = set([1,2,3])
print(s1.issubset(s2)) # False
print(s2.issubset(s1)) # True
헷갈리지 말자, src가 target의 subset이냐?를 확인하는 것이다. 반대로, target이 src의 subset이냐?는 src.issuperset(target)
으로 체크할 수 있다. 이는 반대로 src가 target의 슈퍼셋이냐는 말이다.
s1 = set([1, 2, 3 ,4 ,5 ,6])
s2 = set([1,2,3])
print(s1.issuperset(s2)) # True
print(s2.issuperset(s1)) # False
isdisjoint
를 사용하면 교집이 없으면 True, 있으면 False를 반환한다. 즉, 이 집합 둘이 떨어져 있냐(해제되어 있냐를 검사한다)
s1 = set([1, 2, 3 ,4 ,5 ,6])
s2 = set([1,2,3])
print(s1.isdisjoint(s2)) # False
이미 만들어진 set자료형에 값을 추가할 수 있다. 1개의 값만 추가할 경우에는 add
를 사용한다.
s1 = set([1, 2, 3 ,4 ,5 ,6])
s1.add(0)
print(s1) # {0, 1, 2, 3, 4, 5, 6}
여러 개의 값을 추가하려고 할 때는 update
를 사용하면 된다. 단, 입력 값을 리스트로 주어야 한다.
s1 = set([1, 2, 3 ,4 ,5 ,6])
s1.update([1,10,32,0,4,5,-1])
print(s1) # {32, 1, 2, 3, 4, 5, 6, 0, 10, -1}
특정 값을 제거하고 싶을 때는 다음과 같이 remove(원소)
를 사용하면 된다.
s1 = set([1, 2, 3 ,4 ,5 ,6])
s1.remove(1)
print(s1) # {2, 3, 4, 5, 6}
set 역시도 내부가 가변 객체가 안된다해도, 결국 대입문은 레퍼런스를 넘겨주는 방식이다. 때문에 deepcopy
를 써서 원본이 수정되는 문제를 해결해야 한다.
s1 = set([1, 2, 3 ,4 ,5 ,6])
s2 = s1
s2.add(10)
print(s1) # {1, 2, 3, 4, 5, 6, 10}
다음과 같이 set 역시도 얉은 복사(레퍼런스를 넘겨줌)를 한다. 이를 해결하기 위해서는 새로운 set을 만들고 거기에 값만 복사를 하는 deepcopy
를 사용해야 한다.
import copy
s1 = set([1, 2, 3 ,4 ,5 ,6])
s2 = copy.deepcopy(s1)
s2.add(10)
print(s1) # {1, 2, 3, 4, 5, 6}
s1과 s2의 레퍼런스가 다르기 때문에 사본의 변경이 원본에 반영되는 문제가 발생하지 않는다.