2012-10-21 1 views
6

__eq__을 모두 구현하는 클래스가있다 (myClass). 또한 myClass 개체를 약간의 값으로 매핑하는 dict을 가지고 있습니다. 컴퓨팅에는 어느 정도 시간이 걸립니다.`만약 당신이 '키를 독점이라고 부르는 경우 어떻게 될까

내 프로그램 과정 동안 많은 (수백만 단위로) myClass 개체가 인스턴스화됩니다. 따라서 dict을 사용하여 해당 값을 추적합니다.

그러나 때로는 새로운 myClass 개체가 이전 개체 (__eq__ 메서드에 정의 된대로)와 같을 수 있습니다. 따라서 해당 객체의 값을 다시 계산하기보다는 dict에있는 이전 myClass 객체의 값을 조회하고 싶습니다. 이를 수행하기 위해 나는 if myNewMyClassObj in dict을 수행합니다.

여기 내 질문 : 호출되는 것을

내가 in 절 것을 사용, __hash__ 또는 __eq__? dict을 사용하는 시점은 O (1) 조회 시간입니다. 따라서 __hash__을 호출해야합니다. 그러나 __hash____eq__이 동일한 방법이 아닌 경우에는 어떻게해야합니까? 이 경우 if myNewMyClassObj in dict에 대해 거짓 긍정을 표시합니까?

는 질문을 따르

dict에있는 항목의 수를 최소화하려는, 그래서 이상적으로 dict에 해당하는 myClass 오브젝트 세트 중 하나만 유지하고 싶습니다. 그래서 다시, 그것은 if myNewClassObj in dict을 계산할 때 __eq__ 필요가 dict의 O를 더럽히는 것이다,라고하는 것 같다 (1) O 시간을 조회 (n)의 시간을 조회

답변

8

먼저 __hash__(myNewMyClassObj)이 호출됩니다. 동일한 해시를 가진 객체가 사전에 없으면 Python은 myNewMyClassObj이 사전에없는 것으로 간주합니다. (파이썬은 __eq__ 두 객체에 대한 동등한 평가 때마다, 자신의 __hash__이 동일해야합니다 것을 요구합니다.)

을 같은 __hash__에 일부 개체가 사전에 발견 __eq__이 그들 각각에 호출되는 경우. __eq__이 그 중 하나에 대해 동등하다고 평가하면 myNewMyClassObj in dict_은 True를 반환합니다.

따라서 __eq____hash__이 빠르다는 것을 확인하십시오.

다음 질문에 답하십시오. 예 : dict_MyClass 개체 (__eq__에 정의 된대로) 중 하나만 저장합니다. (설정된대로)

동일한 해시를 갖고 동일한 버킷에 할당 된 개체에만 __eq__이 호출된다는 점에 유의하십시오. 이러한 객체의 수는 대개 매우 적습니다 (dict 구현을 참조하십시오). 따라서 여전히 (대략) O(1) 조회 성능이 있습니다.

7
__hash__ 항상 호출됩니다

; __eq__은 개체가 실제로 사전에 있거나, 해시가 동일한 다른 개체가 사전에 있으면 호출됩니다. 해시 값은 가능한 키의 선택 범위를 좁히는 데 사용됩니다. 키는 해시 값에 의해 "버킷"으로 그룹화되지만 조회를 위해 여전히 파이썬은 버킷의 각 키를 조회 키와 동일한 지 확인해야합니다. http://wiki.python.org/moin/DictionaryKeys을 참조하십시오. 이 예제 봐 :

>>> class Foo(object): 
...  def __init__(self, x): 
...   self.x = x 
...  
...  def __hash__(self): 
...   print "Hash" 
...   return hash(self.x) 
... 
...  def __eq__(self, other): 
...   print "Eq" 
...   return self.x == other.x 
>>> Foo(1) in d 
Hash 
Eq 
10: True 
>>> Foo(2) in d 
Hash 
Eq 
11: True 
>>> Foo(3) in d 
Hash 
Eq 
12: True 
>>> Foo(4) in d 
Hash 
13: False 

을 그 예에서, 당신은 항상 __hash__라고 볼 수 있습니다. __eq__은 객체가 dict에있을 때 각 조회마다 고유 한 해시 값을 가지고 있기 때문에 한 번 호출되므로 해시 값이 true 인 객체가 실제로 조회되는 객체인지 확인하는 데 충분합니다. __eq__은 마지막 사례에서 호출되지 않습니다. 왜냐하면 사전에있는 객체 중 어느 것도 Foo(4)과 동일한 해시 값을 가지지 않기 때문에 파이썬은 __eq__을 계속 사용할 필요가 없습니다.

>>> class Foo(object): 
...  def __init__(self, x): 
...   self.x = x 
...  
...  def __hash__(self): 
...   print "Hash" 
...   return 1 
... 
...  def __eq__(self, other): 
...   print "Eq" 
...   return self.x == other.x 
>>> d = {Foo(1): 2, Foo(2): 3, Foo(3): 4} 
Hash 
Hash 
Eq 
Hash 
Eq 
Eq 
>>> Foo(1) in d 
Hash 
Eq 
18: True 
>>> Foo(2) in d 
Hash 
Eq 
Eq 
19: True 
>>> Foo(3) in d 
Hash 
Eq 
Eq 
Eq 
20: True 
>>> Foo(4) in d 
Hash 
Eq 
Eq 
Eq 
21: False 

이 버전에서 모든 개체는 동일한 해시 값을가집니다. 이 경우 __eq__은 해쉬가 값을 구별하지 않기 때문에 항상 여러 번 호출되기 때문에 파이썬은 동등한 값을 찾을 때까지 명시 적으로 모든 값에 대해 동등성을 검사해야합니다. 찾는 사람). 때로는 첫 번째 시도 (Foo(1) in dict 위)에서 찾으면 모든 값을 확인해야하는 경우가 있습니다.

+0

@MartijnPieters : 나는 우연히 그들을 포함하기 전에 저장을 누르십시오, 그들은 지금 있습니다. – BrenBarn

+0

환상적인 예! – inspectorG4dget

+1

파이썬은 해시 테이블에서 버킷을 사용하지 않습니다. 단일 값을 포함하는 각 슬롯에 슬롯을 사용합니다. 슬롯이 가득 차면 일치하는 슬롯이나 사용되지 않은 슬롯을 찾을 때까지 다른 슬롯을 선택합니다. – Duncan

1

__hash__ __eash__는 객체가 들어있는 버킷을 정의하고 __eq__는 객체가 동일한 버킷에있을 때만 호출됩니다.