Python hash () 함수 내장


82

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)에서 동일한 결과를 제공하는 해시 함수를 어떻게 가질 수 있습니까?


14
구글의가있는 동안이 당신의 WINXP는 32 비트 플랫폼 사실을 빚 64 비트
Tzury 바 Yochay

답변:


56

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

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

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


5
hashlib비 암호화 사용을 위해 해시 함수 가 약간 느리지 않습니까?
Brandon Rhodes

8
Jenkins, Bernstein, FNV, MurmurHash 등과 같은 범용 해시 함수에 비해 실제로 매우 느립니다. 자신 만의 해시 테이블과 같은 구조를 만들고자 한다면
lericson

45
벤치 마크 : hash95 NS, binascii.crc32570 ns의, hashlib.md5.digest()1.42 우리를, murmur.string_hash234 ns의
temoto

hash각 파이썬 세션에서 무작위로 생성 된 새로운 솔트 값을 사용합니다. 따라서 파이썬 세션간에 변경됩니다.
호브

89

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

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

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

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


감사합니다! 나는 왜 동일한 객체에 대해 항상 다른 해시 값을 가져 와서 딕셔너리로 ​​예기치 않은 동작을 발생시키는 이유를 궁금해했습니다. hashlib.md5에서 고유 한 int 해시를 생성하는 빠른 방법 은 다음과 같습니다 (객체가 동일한 경우 동일한 것으로 정의 int(hashlib.md5(repr(self)).hexdigest(), 16)되었다고 가정 self.__repr__). 32 바이트가 너무 길면 변환 전에 16 진수 문자열을 슬라이스하여 크기를 줄일 수 있습니다.
Alan Plum

1
두 번째 생각으로, 만약 그것이 __repr__충분히 고유하다면, dicts가 동일한 해시를 가진 같지 않은 객체를 섞지 않기 때문에 str.__hash__(즉 hash(repr(self))) 사용할 수 있습니다 . 이것은 repr이 분명히 신원을 나타낼 수있을 정도로 객체가 사소한 경우에만 작동합니다.
Alan Plum

그래서, 두 개체와 예에서 ab, 어떻게 오브젝트가 동일한 지 확인하기 위해 hashlib 모듈을 사용할 수 있습니다?
Garrett


32

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

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에서와 같이 "순환"곱셈 (오버플로 없음)입니다.


18

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

기본적 __hash__()으로 str, bytesdatetime객체 의 값은 예측할 수없는 임의의 값으로 "염 처리"됩니다. 개별 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

3
이것은 Python 3.x에만 해당되지만 Python 3이 현재이자 미래이기 때문에이 문제를 해결하는 유일한 답인 +1입니다.
Alexander Huszagh

8

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

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

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

6

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


6

다음은 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

7
이 해시 함수의 용도와 이유에 대한 컨텍스트를 공유 할 수 있습니까?
amcnabb

5

사인 비트는 어떻습니까?

예를 들면 :

16 진수 값 0xADFE74A5은 unsigned 2919134373및 signed를 나타냅니다 -1375832923. Currect 값은 반드시 서명되어야하지만 (sign bit = 1) 파이썬은이를 unsigned로 변환하고 64 비트에서 32 비트로 변환 한 후 잘못된 해시 값을 갖게됩니다.

다음을 사용하여주의하십시오.

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

3

문자열에 대한 다항식 해시. 1000000009239임의의 소수입니다. 우연히 충돌 할 가능성이 없습니다. 모듈 식 산술은 그다지 빠르지는 않지만 충돌을 방지하려면 2. 물론 의도적으로 충돌을 찾는 것은 쉽습니다.

mod=1000000009
def hash(s):
    result=0
    for c in s:
        result = (result * 239 + ord(c)) % mod
    return result % mod

2

PYTHONHASHSEED 값을 사용하여 해시 값을 초기화 할 수 있습니다.

시험:

PYTHONHASHSEED python -c 'print(hash('http://stackoverflow.com'))'

-3

자체 알고리즘이 아닌 운영 체제에서 제공하는 기능을 묻습니다.

다른 의견이 말했듯이 hashlib를 사용 하거나 자신의 해시 함수를 작성하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.