Python 기초

세트(Set)

세트(set)는 우리가 수학에서 배웠던 집합입니다. 세트는 중복되지 않은 항목들이 모인 것입니다.

세트의 항목 간에는 순서가 없습니다. 만약 응용 프로그램에서 순서 없는 항목들의 집합을 원한다면 세트가 최선의 선택이 됩니다.

하지만 중복된 항목은 없어야 합니다. 파이썬에서 세트를 생성하려면 요소들을 중괄호 기호 { ... } 로 감싸면 됩니다.

 

집합에 대한 이미지 검색결과

 

세트 = { 항목1, 항목2, ... , 항목n }

 

세트는 중괄호 기호 안에 항목들을 쉼표로 분리하여 놓으면 됩니다.

numbers = {2, 1, 3}
print(numbers)

 

<실행 결과>

{1, 2, 3}

 

세트의 크기는 len() 함수로 알 수 있습니다.

numbers = {2, 1, 3}
print(len(numbers))

 

<실행 결과>

3

 

세트를 만들 때, 문자열로 이루어진 세트도 생성할 수 있으며 여러 가지 자료형을 섞어도 됩니다.

fruits = {"Apple", "Banana", "Pineapple"}
mySet = { 1.0, 2.0, "Hello World", (1, 2, 3) }

 

세트는 집합이기 때문에 요소가 중복되면 자동으로 중복된 요소를 제거합니다.

cities = {"Paris", "Seoul", "London", "Berlin", "Paris", "Seoul"}
print(cities)

 

<실행 결과>

{'Seoul', 'London', 'Berlin', 'Paris'}

 

비어 있는 세트를 생성하려면 set() 함수를 사용합니다.

numbers = set()

 

어떤 항목이 세트 안에 있는지를 검사하려면 in 연산자를 사용하면 됩니다.

numbers = {2, 1, 3}

if 1 in numbers:
    print("집합 안에 1이 있습니다.")

 

<실행 결과>

집합 안에 1이 있습니다.

 

세트의 항목은 순서가 없기 때문에 위치를 가지고 세트의 항목에 접근할 수는 없습니다. 하지만 for 반복문을 이용하여 각 항목들에 접근할 수 있습니다.

numbers = {2, 1, 3}

for x in numbers:
    print(x, end=" ")

 

<실행 결과>

1 2 3

 

여기서 주의할 점은 항목들이 출력되는 순서는 입력된 순서와 다를 수도 있다는 점입니다. 우리의 예제에서도 입력된 순서는 2, 1, 3이지만 출력되는 순서는 1, 2, 3입니다.

 

세트는 모든 요소들을 해싱(hashing)을 이용하여 저장하고 관리합니다. 따라서 요소들은 해싱 가능(hashable)하여야 합니다.

해싱이란 아주 간단히 설명하자면 각각의 객체에 식별할 수 있는 숫자 코드를 부여하여 객체를 테이블에 저장하는 것입니다.

 

파이썬에서 요소가 해싱 가능하려면 해쉬 코드를 가져야 하고 그 값이 변경되면 안 됩니다.

따라서 세트는 변경 가능한 항목을 가지면 안 됩니다. 예를 들어서 세트 안에 리스트를 넣으면 안 됩니다.

numbers = {1, 2, [3, 4, 5]}

 

<실행 결과>

TypeError: unhashable type: 'list'

 

하지만 리스트로부터 세트를 생성하는 것은 가능합니다.

print(set([1, 2, 3, 1, 2, 3]))

 

<실행 결과>

{1, 2, 3}

 

문자열로부터 세트를 생성하는 것도 가능합니다.

print(set("abcdefa"))

 

<실행 결과>

{'f', 'a', 'd', 'e', 'c', 'b'}

 

문자열에서는 문자 'a'가 2번 나타나지만 세트는 중복을 허용하지 않으므로 'a'가 한 번만 포함되었습니다.

집합의 요소에 대하여 반복하려면 다음과 같은 문장을 사용하면 됩니다.

for char in set("banana"):
    print(char)

 

<실행 결과>

a
b
n

 

요소 추가하고 삭제하기

세트는 변경 가능한 객체입니다. 따라서 세트에 요소를 추가하거나 삭제할 수 있습니다.

그러나 세트의 요소에는 인덱스가 없기 때문에 인덱싱이나 슬라이싱 연산은 의미가 없습니다.

numbers = {2, 1, 3}
print(numbers[0])

 

<실행 결과>

TypeError: 'set' object does not support indexing

 

우리는 add() 메소드를 이용하여 하나의 요소를 추가할 수 있습니다. 예를 들어 다음과 같음 문장이 가능합니다.

numbers = {2, 1, 3}
numbers.add(4)
print(numbers)

 

<실행 결과>

{1, 2, 3, 4}

 

여러 개의 요소는 update() 메소드로 추가할 수 있습니다. 물론 중복된 요소는 추가되지 않습니다.

numbers = {2, 1, 3}
numbers.update([2, 3, 4, 5])
print(numbers)

 

<실행 결과>

{1, 2, 3, 4, 5}

 

요소를 삭제할 때는 discard() 메소드를 사용할 수 있습니다.

numbers = {2, 1, 3}
numbers.update([2, 3, 4, 5])
numbers.discard(5)
print(numbers)

 

<실행 결과>

{1, 2, 3, 4}

 

remove() 메소드도 사용할 수 있습니다. 만약 세트에 없는 요소를 삭제하려고 하면 remove()는 예외를 발생시킵니다.

numbers = {2, 1, 3}
numbers.remove(6)    # 세트에 6이 없으므로 예외가 발생합니다.
print(numbers)

 

<실행 결과>

KeyError: 6

 

clear() 메소드를 세트의 전체 요소를 지웁니다.

numbers = {2, 1, 3}
numbers.clear()    # 세트의 크기가 0이 됩니다.
print(numbers)

 

<실행 결과>

set()

 

부분 집합 연산

2개의 세트가 같은지도 검사할 수 있습니다. 이것은 == 와 != 연산자를 사용하는 것이 가장 쉽습니다.

A = {1, 2, 3}
B = {1, 2, 3}
print(A == B)

 

<실행 결과>

True

 

< 와 <= 연산자를 사용하면 세트가 진부분 집합인지, 부분 집합인지를 검사할 수 있습니다. >와 >= 연산자를 사용하면 진상위 집합, 상위 집합도 검사할 수 있습니다.

A = {1, 2, 3, 4, 5}
B = {1, 2, 3}
print(A > B)

 

<실행 결과>

True

 

부분집합인지를 검사하는 메소드는 issubset() 입니다.

A = {1, 2, 3, 4, 5}
B = {1, 2, 3}
print(B.issubset(A))

 

<실행 결과>

True

 

상위집합인지를 검사하는 메소드는 issuperset()입니다.

A = {1, 2, 3, 4, 5}
B = {1, 2, 3}
print(A.issuperset(B))

 

<실행 결과>

True

 

요소가 집합에 포함되어 있는지는 in 키워드를 이용하여 검사할 수 있습니다.

mySet = set("banana")
print('a' in mySet)
print('p' not in mySet)

 

<실행 결과>

True
True

 

집합 연산

세트가 유용한 이유는 교집합이나 합집합과 같은 여러 가지 집합 연산을 지원하기 때문입니다. 이것은 연산자나 메소드로 수행할 수 있습니다.

일단 다음과 같은 2개의 집합이 세트로 정의되어 있다고 가정합시다.

A = {1, 2, 3}
B = {3, 4, 5}

 

합집합은 2개의 집합을 합하는 연산입니다. 물론 중복되는 요소는 제외됩니다.

합집합에 대한 이미지 검색결과

 

합집합은 | 연산자나 union() 메소드를 사용합니다.

A = {1, 2, 3}
B = {3, 4, 5}

print(A | B)
print(A.union(B))
print(B.union(A))

 

교집합은 2개의 집합에서 겹치는 요소를 구하는 연산입니다. 교집합은 & 연산자나 intersection() 메소드를 사용합니다.

교집합에 대한 이미지 검색결과

 

A = {1, 2, 3}
B = {3, 4, 5}

print(A & B)
print(A.intersection(B))

 

<실행 결과>

{3}
{3}

 

차집합은 하나의 집합에서 다른 집합의 요소를 뺴는 것입니다. 차집합은 - 연산자나 difference() 메소드를 사용합니다.

차집합에 대한 이미지 검색결과

A = {1, 2, 3}
B = {3, 4, 5}

print(A - B)
print(A.difference(B))

 

<실행 결과>

{1, 2}
{1, 2}

 

다른 메소드

집합에 대해서도 all(), any(), enumerate(), len(), max(), min(), sorted(), sum() 등의 메소드는 사용할 수 있습니다.

all()은 세트의 모든 요소가 True인 경우에 세트다 True가 됩니다. any()는 하나의 요소라도 True이면 True를 반환합니다.

 

도전 과제

1. 점술회 회장 봉민이는 어제와 오늘 점술회에 참석한 사람들의 명단을 작성하기 위해 세트 A와 B에 이름을 각각 저장하였습니다.

오늘은 김봉민, 김태민, 이주형, 임연수 네 사람이 참여했고 어제는 김봉민, 이주형, 임연수, 임채언 네 사람이 참여했습니다.

세트를 이용하여 어제와 오늘 모임에 모두 참석한 사람들의 명단을 출력해봅시다.

 

<실행 결과>

2개의 모임에 모두 참여한 사람은 다음과 같습니다.
{'김봉민', '이주형', '임연수'}

 


2. One Republic의 "Counting Stars" 라는 노래의 가사를 텍스트 파일에 저장한 후 단어를 얼마나 다양하게 사용하여 가사를 작성하였는지 계산하는 프로그램을 작성해봅시다.

중복된 단어는 하나만 인정합니다. 대문자는 소문자로 바꾸어주고 구두점은 제거합니다. 아래와 같은 의사코드를 생각할 수 있습니다.

공백 세트를 생성합니다.
텍스트 파일의 각 단어에 대하여 반복합니다.
    단어를 집합에 추가합니다.
단어의 개수는 집합의 크기가 됩니다.

 

집합은 중복을 허용하지 않기 때문에 집합에 추가하면 중복되지 않은 단어가 몇 개나 사용되었는지를 알 수 있습니다.

아래는 "Counting Stars" 의 가사입니다. 파일 이름은 CountingStars.txt 로 지정합니다.

Lately I been, I been losing sleep
Dreaming about the things that we could be
But baby I been, I been prayin' hard
Said no more counting dollars
We'll be counting stars
Yeah, we'll be counting stars
I see this life
Like a swinging vine
Swing my heart across the line
In my faces flashing signs
Seek it out and ye shall find
The old, but I'm not that old
Young, but I'm not that bold
And I don't think the world is sold
I'm just doing what we're told
I, feel something so right
Doing the wrong thing
I, feel something so wrong
But doing the right thing
I could lie, could lie, could lie
Everything that kills me makes me feel alive
Lately I been, I been losing sleep
Dreaming 'bout the things that we could be
Baby I been, I been prayin' hard
Said no more counting dollars
We'll be counting stars
Lately I been, I been losing sleep
Dreaming 'bout the things that we could be
But baby I been, I been prayin' hard
Said no more counting dollars
We'll be, we'll be counting stars
I feel the love
And I feel it burn
Down this river every turn
Hope is a four letter word
Make that money
Watch it burn
Old, but I'm not that old
Young, but I'm not that bold
And I don't think the world is sold
I'm just doing what we're told
I, feel something so wrong
But doing the right thing
I could lie, could lie, could lie
Everything that drowns me makes me wanna fly
Lately I been, I been losing sleep
Dreaming about the things that we could be
Baby I been, I been prayin' hard
Said no more counting dollars
We'll be counting stars
Lately I been, I been losing sleep
Dreaming 'bout the things that we could be
But baby I been, I been prayin' hard
Said no more counting dollars
We'll be, we'll be counting stars
Take that money and watch it burn
Sink in the river the lessons I learned
Take that money and watch it burn
Sink in the river the lessons I learned
Take that money and watch it burn
Sink in the river the lessons I learned
Take that money and watch it burn
Sink in the river the lessons I learned
Everything that kills me makes me feel alive
Lately I been, I been losing sleep
Dreaming 'bout the things that we could be
Baby I been, I been prayin' hard
Said no more counting dollars
We'll be counting stars
Lately I been, I been losing sleep
Dreaming 'bout the things that we could be
Baby I been, I been prayin' hard
Said no more counting dollars
We'll be, we'll be counting stars

 

<실행 결과>

입력 파일 이름 : CountingStars.txt
사용된 단어의 개수 =  96
{'just', 'word', 'find', 'sold', 'everything', 'lately', 'me', 'about', 'turn', 'fly', 'but', 'hard', 'prayin', 'watch', 'life', 'something', 'thing', 'well', 'river', 'what', 'been', 'like', 'is', 'seek', 'four', 'feel', 'told', 'more', 'baby', 'old', 'alive', 'doing', 'love', 'my', 'dont', 'im', 'money', 'said', 'that', 'sleep', 'burn', 'young', 'hope', 'we', 'swing', 'be', 'line', 'could', 'dreaming', 'things', 'in', 'lie', 'heart', 'and', 'faces', 'so', 'world', 'kills', 'makes', 'sink', 'bout', 'vine', 'flashing', 'no', 'take', 'lessons', 'not', 'make', 'stars', 'dollars', 'down', 'think', 'learned', 'wanna', 'see', 'every', 'drowns', 'ye', 'across', 'wrong', 'it', 'yeah', 'right', 'shall', 'this', 'letter', 'counting', 'signs', 'i', 'a', 'bold', 'the', 'out', 'were', 'losing', 'swinging'}

 

HINT

파일을 열고 모든 줄을 읽는 코드는 아래와 같습니다.

# 파일을 엽니다.
fname = input("입력 파일 이름 : ")
file = open(fname, "r")

# 파일의 모든 줄에 대하여 반복합니다.
for line in file:
    lineWords = line.split()

    for word in lineWords:
        # 이 곳에서 단어를 세트에 추가합니다.
        # 파일을 처리하는 코드를 이 곳에 작성합니다.