python hash 함수에 대해 알아봅시다.

코딩/파이선 2023. 6. 19. 23:50

 python에는 hash 함수가 있습니다. 무엇을 하는 함수일까요?

 


 간단하게 hash(1)과 hash('str')을 입력해 보겠습니다.

 

 그랬더니 1과 -34797.. 이 나옵니다. 객체의 hash값을 돌려주기 위해서 쓴다. 정도로 추정할 수 있습니다.

 

 

 이제, Obj 하나를 정의해 보겠습니다. 생성자에 인자 x를 받습니다. 그러면 Obj의 필드 x에 x의 값을 넣습니다. 이제 Obj(1)의 해시값과, 다른 객체인 Obj(1)의 해시값을 비교해 보겠습니다.

 

 다릅니다. 왜? __hash__가 재정의 되어 있지 않기 때문입니다. 파이썬에서는 dict, counter 등이 hash를 기반으로 동작하는데요. 키 값으로 custom한 객체를 넘겨줄 때 __hash__를 재정의 해야 할 겁니다. 이 __hash__ 안에 hash 함수를 쓸 거라는 예상을 할 수 있습니다.

 


 예제를 하나 보겠습니다.

 

 __eq__랑 __hash__랑 __str__을 재정의 해 주었습니다. __eq__를 정의하면 __hash__도 재정의를 해 주는 것이 국룰입니다. 특히 custom하게 정의한 class의 object를 해시 기반 자료 구조의 key 값으로 쓰려는 경우. 객체가 동등하더라도, hash값이 다르면 다른 hash값을 가지기 때문에, 탐색 시작 위치가 달라져 버릴 수 있기 때문입니다. Obj의 필드는 x 하나밖에 없기 때문에, hash(self.x)를 리턴했습니다만, 2개의 필드에 대해서 해시값을 생성하려는 경우 tuple 등을 생각해 보는 것이 좋습니다.

 

 

 lt에 Obj(1), Obj(1)을 넣어두었습니다. 저는, 이 lt에 있는 것을 set으로 옮겼습니다. 실행 결과가 어떻게 나올까요? 일단 __eq__랑 __hash__를 재정의 했기 때문에, Obj(1)과 뒤에 있는 Obj(1)은 중복 처리가 됩니다.

 

 따라서 Obj: 1만 출력이 됩니다. collection의 Counter도 마찬가지일까요?

 

 c[Obj(1)] += 1 이런 식으로 작성해 주면, 키 Obj(1)의 개수가 하나 증가합니다. 결과는 Obj: 1 = 2, Obj: 2 = 1이 나오게 될 것인데요. Obj가 x 값만 같으면 동일하고, hash 값도 같기 때문입니다.

 

 실행 결과가 제가 예상했던 것과 그대로 나왔네요.

 


 여기서 질문. 이 경우에는 어떨까요? 사실 hash값이 같지만, 동등하지 않는 경우는 의외로 있습니다. 키 3과 5가 같은 hash 값을 가지지만, 동등하지 않은 것과 같은 이치인데요. __eq__가 정의되지 않았으므로, 필드 값이 1이더라도, 별개의 객체인 경우 동등하지 않습니다.

 

 따라서, 위 코드의 경우 Obj(1), Obj(1), Obj(2)가 별개로 count가 됩니다. 한 마디로 hash가 제대로 동작하지 않는다는 것입니다.

 

 실행 결과는 위와 같습니다. 키를 찾기 위한 base 위치는 같겠지만, 필드 x의 값이 같은 별개의 객체 a와 b는 다른 객체이므로, a만 존재하는 경우, b가 없다고 판단하게 됩니다.