Hash?
해시란 단방향 암호화 기법으로 해시함수를 이용하여 고정된 길이의 비트열로 변경한다.
(여기서 단방향 암호화 기법은 암호화는 수행하지만 복호화는 불가능한 알고리즘을 말한다.)
이때 매핑 전 원래 데이터의 값을 키, 매핑 후 데이터의 값을 해시값, 매핑하는 과정을 해싱, 해시값+데이터색인 주소를 해시테이블이라고 한다.
Hashable?
수명 주기 동안, 결코 변하지 않는(immutable) 해시값을 갖고 있고(__hash__ method), 다른 객체와 비교할 수 있으면(__eq__ method) 객체를 해시가능하다고 한다. 동일(==)하다고 판단되는 객체는 반드시 해시값이 동일해야 한다.
Introduction to the Python hash function
먼저 Person 클래스를 name 및 age 특성으로 정의합니다.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
둘째, Person 클래스의 두 인스턴스를 만듭니다.
p1 = Person('John', 22)
p2 = Person('Jane', 22)
셋째, p1 및 p2 객체의 해시를 표시합니다.
print(hash(p1))
print(hash(p2))
Output:
110373112736
110373572343
hash()함수는 객체를 받아들이고 해시 값을 정수로 반환합니다. hash() 함수에 객체를 전달하면 Python은 객체의 __hash__ 특수 메서드를 실행합니다.
이는 p1 객체를 hash() 함수에 전달함을 의미합니다.
hash(p1)
파이썬은 p1 객체의 __hash__ 메서드를 호출합니다:
p1.__hash__()
기본적으로, __hash__는 개체의 ID를 사용하고 __eq__는 두 개체가 동일한 경우 True를 반환합니다.
이 기본 동작을 재정의하려면 __eq__ 및 __hash__ 를 override을 구현할 수 있습니다.
클래스가 __eq__ 메서드를 override하면 클래스의 개체를 해시할 수 없게 됩니다.
Person의 개체는 __eq__ 를 override를 구현하면 __hash__ 는 None으로 설정되기 때문에 해시가 손실됩니다.
예를 들어:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __eq__(self, other):
return isinstance(other, Person) and self.age == other.age
hash(Person('John', 22))
Error:
TypeError: unhashable type: 'Person'
또한 __eq__ method를 override하면 mapping type에서 그 개체를 사용할 수 없음을 의미합니다.
예를 들어 사전의 키나 집합의 요소로 이들을 사용할 수 없습니다. Set에서 Person 개체를 사용하려고 하면 오류가 발생합니다.
members = {
Person('John', 22),
Person('Jane', 22)
}
파이썬은 다음 오류를 발행합니다 :
TypeError: unhashable type: 'Person'
Person 클래스를 __eq__ 메서드 override후 해시 가능하게 만들려면, __hash__ 메서드도 구현해야 합니다.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __eq__(self, other):
return isinstance(other, Person) and self.age == other.age
def __hash__(self):
return hash(self.age)
이제 age에 기반한 equal logic을 지원하고 해시 가능한 Person 클래스를 갖는다.
Person이 사전과 같은 데이터 구조에서 잘 작동하도록 하려면 클래스의 해시가 변경 불가능한 상태로 유지되어야 합니다.
이렇게 하려면 Person 클래스의 age 속성을 read-only property(읽기 전용 속성)으로 만든다.
class Person:
def __init__(self, name, age):
self.name = name
self._age = age
@property
def age(self):
return self._age
def __eq__(self, other):
return isinstance(other, Person) and self.age == other.age
def __hash__(self):
return hash(self.age)
Summary
- 기본적으로 __hash__는 개체의 ID를 사용하여 해시값을 반환한다. ( __eq__는 비교를 위해 is 연산자 를 사용 )
- __eq__ 구현(override)하는 경우, __hash__ 를 구현하지 않으면 Python은 __hash__를 None으로 설정합니다.
[해결책] __hash__ 메서드를 override 구현 - __eq__ 구현(override)하는 경우, mapping type에서 그 개체를 사용할 수 없다.
[해결책] 해시가 변경 불가능한 상태로 유지해야함, hash함수 관련 속성을 read-only property(읽기 전용 속성)으로 설정하고, __hash__ 구현
'Python Object-oriented Programming' 카테고리의 다른 글
Dependency Inversion Principle (0) | 2023.04.04 |
---|---|
__bool__ Method (0) | 2023.04.03 |
__eq__ Method (0) | 2023.04.03 |
__repr__ Method (0) | 2023.04.03 |
__str__ Method (0) | 2023.04.03 |
댓글