Python hash () işlevinde yerleşik


83

Windows XP, Python 2.5:

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

Google Uygulama Motoru ( http://shell.appspot.com/ ):

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

Neden? Farklı platformlarda (Windows, Linux, Mac) bana aynı sonuçları verecek bir hash fonksiyonuna nasıl sahip olabilirim?


14
bu,
winxp'inizin

Yanıtlar:


57

Hashlib'i aşağıdakiler hash() için tasarlandığı gibi kullanın :

sözlük araması sırasında sözlük tuşlarını hızla karşılaştırın

ve bu nedenle Python uygulamalarında aynı olacağını garanti etmez.


5
Hash fonksiyonları hashlibkriptografik olmayan kullanım için biraz yavaş değil mi ?
Brandon Rodos

8
Jenkins, Bernstein, FNV, MurmurHash ve diğerleri gibi genel amaçlı hash işlevlerine kıyasla aslında çok yavaştırlar. Kendi hash tablo benzeri yapınızı oluşturmak istiyorsanız
lericson

46
Karşılaştırmalar: hash95 ns, binascii.crc32570 ns, hashlib.md5.digest()1,42 us, murmur.string_hash234 ns
temoto

hashher python oturumunda rastgele oluşturulmuş yeni bir tuz değeri kullanır. Yani python oturumları arasında değişecek.
ocak

89

Belgelerinde belirtildiği üzere, dahili karma () işlevi olan olmayan bir yerde dıştan elde edilen karma depolamak için dizayn edilmiştir. Nesnenin hash değerini sağlamak, sözlüklerde saklamak vb. İçin kullanılır. Ayrıca uygulamaya özgüdür (GAE, Python'un değiştirilmiş bir sürümünü kullanır). Çıkış yapmak:

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

Gördüğünüz gibi, hash () __hash__SHA gibi 'normal' karma algoritmalar yerine nesnenin yöntemini kullandığından farklıdırlar .

Yukarıdakiler göz önüne alındığında, rasyonel seçim, hashlib modülünü kullanmaktır .


Teşekkür ederim! Buraya neden aynı nesneler için her zaman farklı hash değerleri elde ettiğimi merak ederek geldim, bu da diktlerle beklenmedik davranışlara neden oluyor (eşitliği kontrol etmek yerine hash + türüne göre indeks). Hashlib.md5'ten kendi int hash'inizi oluşturmanın hızlı bir yolu int(hashlib.md5(repr(self)).hexdigest(), 16)( self.__repr__nesnelerin özdeş olduğu varsayılırsa özdeş olarak tanımlandığı varsayılır ). 32 bayt çok uzunsa, dönüştürmeden önce onaltılık dizeyi dilimleyerek boyutu elbette azaltabilirsiniz.
Alan Plum

1
İkinci düşünceye göre, __repr__yeterince benzersizse, diktler eşit olmayan nesneleri aynı hash ile karıştırmadığı için str.__hash__(ie hash(repr(self))) kullanabilirsiniz . Bu, yalnızca nesne, repr açıkça kimliği temsil edebilecek kadar önemsizse işe yarar.
Alan Plum

Yani, iki nesne ile örnekte ave b, nasıl nesneleri aynı olduğunu görmek için hashlib modülünü kullanabilirsiniz?
Garrett


32

Cevap kesinlikle şaşırtıcı değil: aslında

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

Bu nedenle , ASCII dizgilerinde güvenilir yanıtlar almak istiyorsanız , en düşük 32 biti uint. Dizeler için hash işlevi 32-bit güvenlidir ve neredeyse taşınabilir.

Öte yandan, yöntemi değişmez olarak hash()açıkça tanımlamadığınız herhangi bir nesneyi elde etmeye hiç güvenemezsiniz __hash__.

ASCII dizeleri üzerinden, yalnızca hash, aşağıdaki gibi dizeyi oluşturan tek karakterler üzerinde hesaplandığı için çalışır:

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

burada c_mulfonksiyon C'deki gibi "döngüsel" çarpmadır (taşma olmadan).


18

Cevapların çoğu bunun farklı platformlardan kaynaklandığını öne sürüyor, ancak daha fazlası var. Gönderen belgelenmesiobject.__hash__(self) :

Varsayılan __hash__()olarak str, bytesve datetimenesnelerinin değerleri tahmin edilemeyen rastgele bir değerle "tuzlanmıştır". Tek bir Python sürecinde sabit kalmalarına rağmen, Python'un tekrarlanan çağrıları arasında tahmin edilemezler.

Bu, bir dikte yerleştirmenin en kötü durum performansını, O (n²) karmaşıklığını kullanan, dikkatle seçilmiş girdilerin neden olduğu bir hizmet reddine karşı koruma sağlamayı amaçlamaktadır. Ayrıntılar için http://www.ocert.org/advisories/ocert-2011-003.html adresine bakın.

Karma değerlerini değiştirme yineleme sırasını etkiler dicts, sets ve diğer eşleştirmeleri. Python, bu sıralama hakkında hiçbir zaman garanti vermemiştir (ve genellikle 32 bit ve 64 bit yapılar arasında değişir).

Aynı makinede çalıştırmak bile çağrılarda farklı sonuçlar verecektir:

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

Süre:

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

Ayrıca ortam değişkenine bakın PYTHONHASHSEED:

Bu değişken ayarlanmazsa veya ayarlanmazsa , ve nesnelerinin randomkarmalarını tohumlamak için rastgele bir değer kullanılır .strbytesdatetime

Bir PYTHONHASHSEEDtamsayı değerine ayarlanırsa hash(), karma randomizasyon tarafından kapsanan türlerin oluşturulması için sabit bir çekirdek olarak kullanılır .

Amacı, yorumlayıcının kendisi için kendi kendine testler gibi tekrarlanabilir hashinglere izin vermek veya bir python işlemi kümesinin karma değerleri paylaşmasına izin vermektir.

Tam sayı, aralıktaki bir ondalık sayı olmalıdır [0, 4294967295]. Değerin belirtilmesi, 0hash randomizasyonu devre dışı bırakacaktır.

Örneğin:

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

3
Bu yalnızca Python 3.x için geçerlidir, ancak Python 3 şimdiki zaman ve gelecek olduğu için ve buna hitap eden tek cevap bu olduğu için +1.
Alexander Huszagh

8

Hash sonuçları 32bit ve 64bit platformlar arasında değişir

Hesaplanan bir hash her iki platformda da aynı olacaksa, kullanmayı düşünün

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

6

Tahminime göre, AppEngine 64 bit Python uygulaması kullanıyor (-5768830964305142685, 32 bite sığmaz) ve sizin Python uygulamanız 32 bit. Nesne karmalarının farklı uygulamalar arasında anlamlı bir şekilde karşılaştırılabilir olmasına güvenemezsiniz.


6

Bu, Google'ın python 2.5 için üretimde kullandığı hash işlevidir:

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
Bu hash işlevinin ne için ve neden kullanıldığı hakkında herhangi bir bağlam paylaşabilir misiniz?
amcnabb

5

Ya işaret biti?

Örneğin:

Onaltılık değer 0xADFE74A5işaretsiz 2919134373ve işaretli olduğunu gösterir -1375832923. Geçerli değer işaretli olmalıdır (işaret biti = 1) ancak python onu işaretsiz olarak dönüştürür ve 64'ten 32 bit'e çeviri sonrasında yanlış bir hash değerimiz olur.

Kullanırken dikkatli olun:

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

3

Dizeler için polinom hash. 1000000009ve 239keyfi asal sayılardır. Kazayla çarpışma olasılığı düşüktür. Modüler aritmetik çok hızlı değildir, ancak çarpışmaları önlemek için modülo bir kuvvet almaktan daha güvenilirdir 2. Elbette kasıtlı olarak bir çarpışma bulmak kolaydır.

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

2

PYTHONHASHSEED değeri , hash değerlerini başlatmak için kullanılabilir.

Deneyin:

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

-3

Muhtemelen kendi algoritmasından ziyade sadece işletim sisteminin sağladığı işlevi sorar.

Diğer yorumların da söylediği gibi, hashlib kullanın veya kendi hash fonksiyonunuzu yazın.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.