파이썬을 배워보자 7일차 - set(집합)

0

Python

목록 보기
7/18

점프 투 파이썬 : https://wikidocs.net/book/1
파이썬 기본을 갈고 닦자 : https://wikidocs.net/16031

set(집합)

수학의 집합을 쉽게 사용하기위해 만들어진 자료구조이다. 집합과 같은 특성을 지니는데 다음과 같다.

  • 순서가 없고, 집합 안의 값은 unique하다. 즉 중복을 허용하지 않는다.
  • 가변적이다. 즉 mutable하다.
  • set()또는 {}을 통해 선언할 수 있다. 다만 {}의 경우 dictionary와 헷갈리므로 {값}으로 적어주야 한다. 즉 key가 없다.

1. 집합의 선언

집합은 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 두 개 중 하나만 살아남고 하나는 사라진 것을 확인할 수 있다. 이 처럼 집합은 중복을 허용하지 않는다.

또한, 순서가 의미가 없기 때문에 인덱싱 또한 지원하지 않는다.

2. 집합 연산

set은 집합 연산을 하기위해 사용하는데, 집합 연산이 자주 사용되는 것은 아니더라도, 알아두면 매우 유용하다.

2.1 교집합

교집합은 두 집합의 공통된 부분을 떼어내는 것으로 & 또는 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}

2.2 합집합

합집합은 두 집합을 합치는 것이다. 중복된 값은 하나만 나오게 된다. | 또는 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}

2.3 차집합

차집합은 두 집합의 공통된 부분을 제거하고 앞의 집합에 해당하는 원소만 남기는 연산이다. - 또는 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}

2.4 대칭 차집합

먼저 합집합을 시켜놓고 교집합을 빼는 것이다. 차집합이 연산 앞에 있는 집합의 나머지라면 이건 둘의 나머지가 되는 것이다. ^ 또는 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등이 가능하다.

2.5 부분집합 여부 확인

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

2.6 교집합 없는 지 여부 확인

isdisjoint를 사용하면 교집이 없으면 True, 있으면 False를 반환한다. 즉, 이 집합 둘이 떨어져 있냐(해제되어 있냐를 검사한다)

s1 = set([1, 2, 3 ,4 ,5 ,6])
s2 = set([1,2,3])

print(s1.isdisjoint(s2)) # False

3. 집합 관련 메서드들

3.1 값 추가하기(add, update)

이미 만들어진 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}

3.2 값 제거하기(remove)

특정 값을 제거하고 싶을 때는 다음과 같이 remove(원소)를 사용하면 된다.

s1 = set([1, 2, 3 ,4 ,5 ,6])
s1.remove(1)
print(s1) # {2, 3, 4, 5, 6}

4. set의 복사

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의 레퍼런스가 다르기 때문에 사본의 변경이 원본에 반영되는 문제가 발생하지 않는다.

0개의 댓글