파이썬3 노트

모듈/함수 공부: [__hash__()][Collections-Counter]

Jonchann 2018. 8. 11. 11:52

파이썬 도큐멘트에 따르면 이 모듈은 파이썬 내장 컨테이너[각주:1] dict, list, set, tuple을 대신하는 특수한 컨테이너 데이터형을 구현하기 위한 것이라 한다. 다 보면 좋겠지만 당장 필요한 것은 Counter에 대한 지식이므로 다른 것은 스킵!




[Counter]

: hashable한 오브젝트를 세는 사전의 서브클래스



[__hash__()]

대체 hashable한 것이 무언가 싶어 찾아보니 hash function이 나왔다. 위키피디아에 따르면 '임의의 길이의 데이터를 고정된 길이의 데이터로 매핑하는 함수'라 한다. 이건 한국어판 위키고 일본어판을 보면 '어떤 데이터가 주어졌을 때 그 데이터를 대표하는 수치를 얻는 조작 또는 그러한 수치를 얻기 위한 함수'라고 나와있다. 이거 좀 다른거 아닌가..?? 내가 아직 프로그래밍에 대한 이해가 부족해서 그런가..


어쨌든 일본어 설명을 기준으로(검색해서 자세한 설명이 잘 나오는 건 qiita이므로) '데이터를 대표하는' 이라는 말의 의미는 hash 함수로 얻을 수 있는 값이

if a == b then hash(a) == hash(b)

라는 조건을 만족하는 것이라 한다. 더 모르겠다.


어쨌든 파이썬의 '사전'에 있어서 해쉬 함수란 무엇일까. 간단히 말하면 딕셔너리의 key를 비교하는 함수라는 것 같다.

딕셔너리에서 값을 설정하고 얻을 때 == 에 의한 사전 전체 검색 대신 저렴하게 처리할 수 있게끔 해쉬함수를 사용한다고 한다. 즉,


1) 값을 설정할 때 덮어쓸까 추가할까 판단할 때 사용


* 해쉬 값은 일치하는데 == 로 일치하는 값은 없을 때 → 새로운 key를 추가

* 해쉬 값이 일치하면서 == 로도 일치하는 값이 있을 때 → 기존의 key 값에 덮어쓰기


2) 값을 얻을 때 일치하는 값인지 판단할 때 사용


* 해쉬 값은 일치하는데 == 로 일치하는 값은 없을 때 → 에러

* 해쉬 값이 일치하면서 ==로도 일치하는 값이 있을 때 → 값을 return


이렇게 __hash__()라는 메소드를 사용할 수 있다면 다 hashable하다고 하는 것일까.

그건 아니라 한다. hash 값이 생존기간 중 변하지 않아야 한다고 하는데 도대체 설명이 이해가 안간다. 그러니 간단히 qiita에 적혀 있는 것을 베껴오자면, 


* hashable: int, str, tuple, frozenset

* unhashable: list, dict, set


라 하는데 이러한 특징과 불변성을 관련지어 생각해보자(라고 나와있다).


hashable한 내장 오브젝트는 불변적이다(불변성을 가지므로 hash 값이 변하지 않는다고 보증할 수 있다).


* 불변적: int, str, tuple, frozenset

* 가변적: list, dict, set


hashable한가 하지 않는가 분류한 것과 결과가 똑같다..!

어쨌든 똥 싸다 끊긴 기분이지만 이러한 속성을 갖는 메소드(함수?)가 hash라 한다.



[Counter]

그럼 다시 카운터로 돌아와서.

Counter란 hashable한 오브젝트를 카운트 하는 dict의 서브클래스이다. 즉, '요소'를 딕셔너리의 key로 보존하고 카운트한 숫자를 값으로 보존한다. 순서는 따지지 않는다.

Counter는 요소를 반복적인 것을 세거나 다른 매핑으로 초기화할 수 있다:

c = Counter() #새 empty Counter
c = Counter("python") #가변적인 새 Counter
c = Counter({'red':4, 'black':3}) #매핑으로 만든 새 Counter
c = Counter(cats = 4, dogs = 8) #키워드 변수로 만든 새 counter


위와 같이 Conter 오브젝트는 딕셔너리 형태를 인수로 가질 수 있는데 존재하지 않는 요소에 대해 KeyError를 출력하는 대신 0을 return한다는 점이 일반적인 딕셔너리와 다르다.

c = Counter(['eggs', 'ham'])
print(c['bacon']) #0
print(c['eggs']) #1
print(c['dogs']) #1

; 출력하는 값은 그 단어가 나타나는 빈도 수이다.


그렇다고  c['bacon'] = 0 이라 초기화 한다 한들 삭제되는 것은 아니다. 제대로

del c['eggs']

해줘야 삭제된다.


Conter 오브젝트에는 모든 딕셔너리에서 이용할 수 있는 메소드 + 다음의 3 개 메소드를 이용할 수 있다.


1) elements()


각각의 요소를 카운트한 횟수 만큼 반복하는 iterator를 return한다. 요소는 임의의 순서로 return된다. 어떤 요소의 카운트 횟수가 1 미만이라면 elements()는 그 요소를 무시한다.

c = Counter(a=4, b=2, c=0, d=-2)
print(sorted(c.elements())) #['a', 'a', 'a', 'a', 'b', 'b']


2) most_common([n])


가장 많이 출현한 n요소를 카운트 수가 높은 순으로 순서대로 나열한 리스트를 return한다. n이 생략되거나 None이라면 most_common()은 Counter의 모든 요소를 return한다. 동일한 카운트 수를 갖는 요소는 임의로 나열한다.

Counter('abracadabra').most_common(3) #[('a', 5), ('r', 2), ('b', 2)] 


3) subtract([iterable-or-mapping])


'요소'에서 중복 요소 또는 매핑 요소는 subtract한다. dict.update()와도 비슷하지만 카운트 수를 변환하는 것이 아니라 빼주는 것이 다르다. 입력도 출력도 0, - 다 가능하다.

c = Counter(a=4, b=2, c=0, d=-2)
d = Counter(a=1, b=2, c=3, d=4)
c.subtract(d)
print(c) #Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6})


보통의 사전 메소드 중 다음의 두 가지 메소드가 Counter에 대해 다른 결과를 가져오는 것만 제외하면 일단 Counter 오브젝트에서도 이용할 수 있다.


1) fromkeys(중복 가능)


이 클래스 메소드는 Counter 오브젝트에는 구현되어있지 않음.


2) update(중복 가능하거나 매핑이거나)


요소가 중복되었다면 그대로 카운트 하거나 다른 매핑이 추가된다. dict.update()와도 비슷하지만 카운트를 변환하는 것이 아니라 추가되는 것이며 중복가능한 것에 대해서는 (key, value)와 같은 시퀀스가 아니라 요소 시퀀스를 구한다.


아래는 Counter 오브젝트를 사용할 때 자주 볼 수 있는 패턴이다.

sum(c.values()) #총 카운트 수
c.clear() #모든 카운트를 리셋
list(c) #uniqe 요소의 카운트 리스트
set(c) #set으로 변환
dict(c) #일반적인 딕셔너리로 변환
c.items() #(elem, cnt)쌍을 갖는 리스트로 변환
Counter(dict(list_of_pairs)) #(elem, cnt) 쌍을 갖는 리스트에서 변환
c.most_common()[:-n-1:-1] #가장 n이 작은 일반 요소
+c #0, - 카운트 제거


Counter 오브젝트와 함께 다중집합(1 이상의 카운트 수를 갖는 Counter)를 만들기 위해 파이썬에서는 몇 가지 수학연산을 할 수 있도록 하고 있다. 

c = Counter(a=3, b=1)
d = Counter(a=1, b=2)
print(c + d) #Counter({'a': 4, 'b': 3})
print(c - d) #Counter({'a': 2})
print(c & d) #교집합 Counter({'a': 1, 'b': 1})
print(c | d) #합집합 Counter({'a': 3, 'b': 2})



  1. 컨테이너란 다른 객체에 대한 참조를 포함하고 있는 객체를 말함. 튜플, 리스트, 딕셔너리 등이 컨테이너에 속하며 '참조'라는 것은 컨테이너 값의 일부이다. 객체의 아이덴티티 보다는 값을 우선하지만 컨테이너의 가변성에 대해 논할 때는 직접 가진 객체들의 아이덴티티만을 따진다. [본문으로]

'파이썬3 노트' 카테고리의 다른 글

[return] [yield] [yield from]  (0) 2018.12.15
pytorch 공부: [nn.Sequential][nn.ModuleList]  (3) 2018.10.16
numpy 공부: [선형대수]  (0) 2018.08.10
numpy 공부: [Indexing] (3)  (0) 2018.08.09
numpy 공부: [Indexing] (2)  (0) 2018.08.08