[python] Python hash () 함수 내장

Windows XP, Python 2.5 :

hash('http://stackoverflow.com') Result: 1934711907

Google App Engine ( http://shell.appspot.com/ ) :

hash('http://stackoverflow.com') Result: -5768830964305142685

왜 그런 겁니까? 다른 플랫폼 (Windows, Linux, Mac)에서 동일한 결과를 제공하는 해시 함수를 어떻게 가질 수 있습니까?



답변

사용 하도록 설계된 대로 hashlib 를 사용하십시오 .hash()

사전 조회 중 사전 키를 빠르게 비교

따라서 Python 구현에서 동일하다는 보장은 없습니다.


답변

문서에서 언급했듯이 내장 hash () 함수는 결과 해시를 외부 어딘가에 저장하도록 설계 되지 않았습니다 . 객체의 해시 값을 제공하고 사전에 저장하는 데 사용됩니다. 또한 구현에 따라 다릅니다 (GAE는 Python의 수정 된 버전을 사용함). 확인 :

>>> class Foo:
...     pass
... 
>>> a = Foo()
>>> b = Foo()
>>> hash(a), hash(b)
(-1210747828, -1210747892)

보시다시피 hash ()는 __hash__SHA와 같은 ‘정상적인’해싱 알고리즘 대신 객체의 메서드를 사용하기 때문에 다릅니다 .

위의 상황에서 합리적인 선택은 hashlib 모듈 을 사용하는 입니다.


답변

반응은 전혀 놀라운 일이 아닙니다. 사실

In [1]: -5768830964305142685L & 0xffffffff
Out[1]: 1934711907L

따라서 ASCII 문자열에 대해 신뢰할 수있는 응답 얻으려면 하위 32 비트를 uint. 문자열에 대한 해시 함수는 32 비트 안전하고 거의 이식 가능합니다.

반면에 메서드를 불변 hash()으로 명시 적으로 정의하지 않은 객체 를 가져 오는 데 전혀 의존 할 수 없습니다 __hash__.

ASCII 문자열에서는 다음과 같이 문자열을 구성하는 단일 문자에 대해 해시가 계산되기 때문에 작동합니다.

class string:
    def __hash__(self):
        if not self:
            return 0 # empty
        value = ord(self[0]) << 7
        for char in self:
            value = c_mul(1000003, value) ^ ord(char)
        value = value ^ len(self)
        if value == -1:
            value = -2
        return value

여기서 c_mul함수는 C에서와 같이 “순환”곱셈 (오버플로 없음)입니다.


답변

대부분의 답변은 이것이 다른 플랫폼 때문이라고 제안하지만 더 많은 것이 있습니다. 에서 의 문서object.__hash__(self) :

기본적 __hash__()으로 str, bytes
datetime객체 의 값은 예측할 수없는 임의의 값으로 “염 처리”됩니다. 개별 Python 프로세스 내에서 일정하게 유지되지만 반복되는 Python 호출 간에는 예측할 수 없습니다.

이는 dict 삽입의 최악의 경우 성능 인 O (n²) 복잡성을 악용하는 신중하게 선택한 입력으로 인한 서비스 거부에 대한 보호를 제공하기위한 것입니다. 자세한 내용은
http://www.ocert.org/advisories/ocert-2011-003.html 을 참조하십시오.

해시 값을 변경하면 반복의 순서에 영향을 dicts, sets
그리고 다른 매핑을. Python은이 순서에 대해 보증 한 적이 없습니다 (일반적으로 32 비트와 64 비트 빌드간에 다릅니다).

동일한 시스템에서 실행하더라도 호출에 따라 다양한 결과가 생성됩니다.

$ python -c "print(hash('http://stackoverflow.com'))"
-3455286212422042986
$ python -c "print(hash('http://stackoverflow.com'))"
-6940441840934557333

동안:

$ python -c "print(hash((1,2,3)))"
2528502973977326415
$ python -c "print(hash((1,2,3)))"
2528502973977326415

환경 변수도 참조하십시오 PYTHONHASHSEED.

이 변수를 설정하지 않거나로 설정하면 , 및 객체 random의 해시를 시드하는 데 임의의 값이 사용됩니다 .strbytesdatetime

경우 PYTHONHASHSEED정수 값으로 설정되어, 그것은 생성 고정 시드로 사용되는 hash()해시 랜덤 적용 유형에있다.

그 목적은 인터프리터 자체에 대한 자체 테스트와 같이 반복 가능한 해싱을 허용하거나 파이썬 프로세스 클러스터가 해시 값을 공유하도록 허용하는 것입니다.

정수는 범위의 10 진수 여야합니다 [0, 4294967295]. 값 0을 지정하면 해시 무작위 화가 비활성화됩니다.

예를 들면 :

$ export PYTHONHASHSEED=0
$ python -c "print(hash('http://stackoverflow.com'))"
-5843046192888932305
$ python -c "print(hash('http://stackoverflow.com'))"
-5843046192888932305


답변

해시 결과는 32 비트와 64 비트 플랫폼간에 다릅니다.

계산 된 해시가 두 플랫폼에서 동일해야하는 경우

def hash32(value):
    return hash(value) & 0xffffffff


답변

추측에 따르면 AppEngine은 64 비트 Python 구현을 사용하고 있으며 (-5768830964305142685는 32 비트에 맞지 않음) Python 구현은 32 비트입니다. 서로 다른 구현간에 의미있게 비교되는 객체 해시에 의존 할 수 없습니다.


답변

다음은 Google이 Python 2.5 용 프로덕션에서 사용하는 해시 함수입니다.

def c_mul(a, b):
  return eval(hex((long(a) * b) & (2**64 - 1))[:-1])

def py25hash(self):
  if not self:
    return 0 # empty
  value = ord(self[0]) << 7
  for char in self:
    value = c_mul(1000003, value) ^ ord(char)
  value = value ^ len(self)
  if value == -1:
    value = -2
  if value >= 2**63:
    value -= 2**64
  return value