Được xây dựng trong hàm băm () trong Python


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

Tại sao vậy? Làm cách nào để có một hàm băm sẽ cho tôi kết quả giống nhau trên các nền tảng khác nhau (Windows, Linux, Mac)?


14
đây là nợ thực tế winxp của bạn là một nền tảng 32bit trong khi google là 64 bit
Tzury Bar Yochay

Câu trả lời:


56

Sử dụng hashlib như hash() được thiết kế để sử dụng :

nhanh chóng so sánh các khóa từ điển trong khi tra từ điển

và do đó không đảm bảo rằng nó sẽ giống nhau trên các triển khai Python.


5
Không phải các hàm băm hashlibhơi chậm đối với việc sử dụng không phải mật mã?
Brandon Rhodes

8
Chúng thực sự rất chậm so với các hàm băm mục đích chung như Jenkins, Bernstein, FNV, MurmurHash và nhiều hàm khác. Nếu bạn đang tìm cách tạo cấu trúc giống bảng băm của riêng mình, tôi khuyên bạn nên xem uthash.h uthash.sourceforge.net
lericson

45
Điểm chuẩn: hash95 ns, binascii.crc32570 ns, hashlib.md5.digest()1,42 us, murmur.string_hash234 ns
temoto

hashsử dụng một giá trị muối mới được tạo ngẫu nhiên với mỗi phiên python. Vì vậy, nó sẽ thay đổi giữa các phiên python.
hobs

89

Như đã nêu trong tài liệu, hàm hash () tích hợp không được thiết kế để lưu trữ các hàm băm kết quả ở đâu đó bên ngoài. Nó được sử dụng để cung cấp giá trị băm của đối tượng, lưu trữ chúng trong từ điển, v.v. Nó cũng dành riêng cho việc triển khai (GAE sử dụng phiên bản Python đã sửa đổi). Thủ tục thanh toán:

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

Như bạn có thể thấy, chúng khác nhau, vì hash () sử dụng __hash__phương thức của đối tượng thay vì các thuật toán băm 'bình thường', chẳng hạn như SHA.

Với những điều trên, sự lựa chọn hợp lý là sử dụng mô-đun hashlib .


Cảm ơn bạn! Tôi đến đây tự hỏi tại sao tôi luôn nhận được các giá trị băm khác nhau cho các đối tượng giống hệt nhau, dẫn đến hành vi không mong muốn với các phần (chỉ mục theo kiểu băm + thay vì kiểm tra sự bằng nhau). Một cách nhanh chóng để tạo hàm băm int của riêng bạn từ hashlib.md5 là int(hashlib.md5(repr(self)).hexdigest(), 16)(giả sử rằng self.__repr__đã được xác định là các đối tượng iff giống hệt nhau thì giống hệt nhau). Nếu 32 byte quá dài, tất nhiên bạn có thể cắt giảm kích thước bằng cách cắt chuỗi hex trước khi chuyển đổi.
Alan Plum

1
Suy nghĩ thứ hai, nếu __repr__đủ duy nhất, bạn có thể chỉ cần sử dụng str.__hash__(tức là hash(repr(self))) vì các phân số không trộn lẫn các đối tượng không bằng nhau với cùng một hàm băm. Điều này chỉ hoạt động nếu đối tượng đủ tầm thường để đại diện có thể đại diện cho danh tính, rõ ràng.
Alan Plum

Vì vậy, trong ví dụ của bạn với hai đối tượng ab, làm thế nào tôi có thể sử dụng mô-đun băm để thấy rằng các đối tượng giống hệt nhau?
Garrett


32

Câu trả lời hoàn toàn không có gì ngạc nhiên: trên thực tế

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

vì vậy nếu bạn muốn nhận được phản hồi đáng tin cậy trên chuỗi ASCII , chỉ cần lấy 32 bit thấp hơn uint. Hàm băm cho chuỗi là 32-bit an toàn và gần như di động.

Mặt khác, bạn hoàn toàn không thể dựa vào việc lấy hash()đối tượng của bất kỳ đối tượng nào mà bạn chưa xác định rõ ràng __hash__phương thức là bất biến.

Trên chuỗi ASCII, nó hoạt động chỉ vì hàm băm được tính toán trên các ký tự đơn lẻ tạo thành chuỗi, như sau:

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

trong đó c_mulhàm là phép nhân "tuần hoàn" (không tràn) như trong C.


18

Hầu hết các câu trả lời cho thấy điều này là do các nền tảng khác nhau, nhưng có nhiều hơn thế. Từ tài liệu củaobject.__hash__(self) :

Theo mặc định, __hash__()giá trị của str, bytesdatetimeđối tượng được “mặn” với một giá trị ngẫu nhiên không thể đoán trước. Mặc dù chúng không đổi trong một quy trình Python riêng lẻ, chúng không thể dự đoán được giữa các lần gọi Python lặp lại.

Điều này nhằm cung cấp sự bảo vệ chống lại sự từ chối dịch vụ gây ra bởi các đầu vào được lựa chọn cẩn thận khai thác hiệu suất trong trường hợp xấu nhất của việc chèn chính tả, độ phức tạp O (n²). Xem http://www.ocert.org/advisories/ocert-2011-003.html để biết thêm chi tiết.

Thay đổi giá trị băm ảnh hưởng đến trật tự lặp của dicts, sets và ánh xạ khác. Python chưa bao giờ đảm bảo về thứ tự này (và nó thường thay đổi giữa các bản dựng 32 bit và 64 bit).

Ngay cả khi chạy trên cùng một máy cũng sẽ mang lại các kết quả khác nhau giữa các lệnh gọi:

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

Trong khi:

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

Xem thêm biến môi trường PYTHONHASHSEED:

Nếu biến này không được thiết lập hoặc thiết lập để randommột giá trị ngẫu nhiên được sử dụng để gieo rắc sự băm của str, bytesdatetimecác đối tượng.

Nếu PYTHONHASHSEEDđược đặt thành một giá trị số nguyên, nó được sử dụng như một hạt giống cố định để tạo ra hash()các loại được bao phủ bởi ngẫu nhiên hóa băm.

Mục đích của nó là cho phép băm có thể lặp lại, chẳng hạn như cho các phép chọn lọc cho chính trình thông dịch hoặc cho phép một nhóm các quy trình python chia sẻ các giá trị băm.

Số nguyên phải là số thập phân trong phạm vi [0, 4294967295]. Chỉ định giá trị 0sẽ vô hiệu hóa ngẫu nhiên hóa băm.

Ví dụ:

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

3
Điều này chỉ đúng với Python 3.x, nhưng vì Python 3 là hiện tại và tương lai và đây là câu trả lời duy nhất giải quyết vấn đề này, +1.
Alexander Huszagh

8

Kết quả băm khác nhau giữa các nền tảng 32bit và 64bit

Nếu một hàm băm được tính toán sẽ giống nhau trên cả hai nền tảng, hãy cân nhắc sử dụng

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

6

Theo dự đoán, AppEngine đang sử dụng triển khai Python 64 bit (-5768830964305142685 sẽ không phù hợp với 32 bit) và việc triển khai Python của bạn là 32 bit. Bạn không thể dựa vào việc băm đối tượng có thể so sánh được một cách có ý nghĩa giữa các cách triển khai khác nhau.


6

Đây là hàm băm mà Google sử dụng trong sản xuất cho 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
Bạn có thể chia sẻ bất kỳ bối cảnh nào về hàm băm này được sử dụng để làm gì và tại sao không?
amcnabb

5

Còn bit ký thì sao?

Ví dụ:

Giá trị hex 0xADFE74A5đại diện cho không dấu 2919134373và có dấu -1375832923. Giá trị cong phải được ký (bit ký = 1) nhưng python chuyển đổi nó thành không có dấu và chúng tôi có giá trị băm không chính xác sau khi dịch từ 64 sang 32 bit.

Hãy cẩn thận khi sử dụng:

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

3

Băm đa thức cho chuỗi. 1000000009239là các số nguyên tố tùy ý. Không có khả năng xảy ra va chạm do tai nạn. Số học mô-đun không nhanh lắm, nhưng để ngăn ngừa va chạm, điều này đáng tin cậy hơn là lấy mô-đun làm lũy thừa 2. Tất nhiên, rất dễ xảy ra va chạm có chủ đích.

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

2

Giá trị của PYTHONHASHSEED có thể được sử dụng để khởi tạo các giá trị băm.

Thử:

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

-3

Nó có thể chỉ hỏi chức năng được cung cấp của hệ điều hành, chứ không phải là thuật toán của riêng nó.

Như các ý kiến ​​khác nói, hãy sử dụng hashlib hoặc viết hàm băm của riêng bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.