Hash python'da ne işe yarar?


88

hashİşlevin bir demete uygulandığı bir kod örneği gördüm . Sonuç olarak, negatif bir tamsayı döndürür. Acaba bu işlev ne yapıyor? Google yardımcı olmuyor. Hash'in nasıl hesaplandığını açıklayan bir sayfa buldum ama bu işleve neden ihtiyacımız olduğunu açıklamıyor.



bu bağlantıya gidin (resmi belgeler). Her şeyi belirtir. bağlantıya git !
tailor_raj

2
Sorunun "o nedir" nin tekrarı değil, "ona neden ihtiyacımız var" olması hoşuma gidiyor.
dnozay

resmi bağlantı çok kafa karıştırıcı
Rasmi Ranjan Nayak

Yanıtlar:


156

Karma, belirli bir değeri tanımlayan sabit boyutlu bir tam sayıdır . Her değerin kendi hash değeri olması gerekir, bu nedenle aynı değer için, aynı nesne olmasa bile aynı hashi elde edersiniz.

>>> hash("Look at me!")
4343814758193556824
>>> f = "Look at me!"
>>> hash(f)
4343814758193556824

Karma değerlerin, elde ettiğiniz karma çarpışmaların sayısını azaltmak için elde edilen değerlerin eşit olarak dağıtılmasını sağlayacak şekilde oluşturulması gerekir. Karma çarpışmalar, iki farklı değerin aynı karmaya sahip olduğu zamandır. Bu nedenle, nispeten küçük değişiklikler genellikle çok farklı karmalarla sonuçlanır.

>>> hash("Look at me!!")
6941904779894686356

Bu sayılar, geniş bir değer koleksiyonundaki değerlerin hızlı bir şekilde aranmasını sağladıkları için çok kullanışlıdır. Kullanımlarının iki örneği Python'un setve dict. Bir de listbir değer listesinde olup olmadığını birlikte, kontrol etmek istiyorsanız, if x in values:Python bütün listeye geçmesi ve karşılaştırmak gerekiyor xlistedeki her değerle values. Bu uzun bir zaman alabilir list. Bir de set, Python her karma izler ve yazarken if x in values:, Python için karma değerini alacak xbir iç yapısında olduğunu aramak ve sonra yalnızca karşılaştırmak xaynı karmaya sahip değerlerle x.

Sözlük araması için aynı metodoloji kullanılır. Bu, aramayı çok hızlı yapar setve dictarama listyavaş olur. Bu ayrıca, bir içinde hashable olmayan nesnelere sahip olabileceğiniz anlamına gelir list, ancak a'da olamaz setveya a'daki anahtarlar olarak olamaz dict. Karma olmayan nesnelerin tipik örneği, değiştirilebilir olan, yani değerini değiştirebileceğiniz herhangi bir nesnedir. Değişken bir nesneye sahipseniz, hash değeri yaşam süresi boyunca değişeceğinden, bir nesne bir sözlükte yanlış karma değerinin altına düşebileceğinden, çok fazla kafa karışıklığına neden olacağından, hashable olmamalıdır.

Bir değerin karmasının yalnızca bir Python çalıştırması için aynı olması gerektiğini unutmayın. Python 3.3'te aslında her yeni Python çalıştırması için değişecekler:

$ /opt/python33/bin/python3
Python 3.3.2 (default, Jun 17 2013, 17:49:21) 
[GCC 4.6.3] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> hash("foo")
1849024199686380661
>>> 
$ /opt/python33/bin/python3
Python 3.3.2 (default, Jun 17 2013, 17:49:21) 
[GCC 4.6.3] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> hash("foo")
-7416743951976404299

Bunu yapmak, belirli bir dizenin hangi hash değerine sahip olacağını tahmin etmek daha zordur, bu, web uygulamaları vb. İçin önemli bir güvenlik özelliğidir.

Karma değerler bu nedenle kalıcı olarak saklanmamalıdır. Karma değerlerini kalıcı bir şekilde kullanmanız gerekiyorsa , dosyaların doğrulanabilir sağlama toplamlarını vb. Yapmak için kullanılabilecek daha "ciddi" karma türlerine, kriptografik karma işlevlerine bir göz atabilirsiniz.


12
Olası hash çarpışmalarıyla ilgili olarak: hash(-1) == hash(-2)(Python 2.7'yi çalıştırın)
Matthias

2
Python 3.6.1 çalıştırıyorum ve çakışma var.
The_Martian

hash(-1) == hash(-2)bugün hala var. Neyse ki, sözlüğü ve arama ayarlarını olumsuz etkilemiyor. Diğer tüm tamsayılar iiçin kendilerine çözmek hash(i)hariç -1.
Chris Conlan

36

TL; DR:

Lütfen sözlüğe bakın : hash()nesneleri karşılaştırmak için kısayol olarak kullanılır, bir nesne diğer nesnelerle karşılaştırılabiliyorsa hashable olarak kabul edilir. bu yüzden kullanıyoruz hash(). Aynı zamanda erişmek için kullanılan dictve setgibi uygulandığı elemanları CPython boyutlandırılabilir karma tablolar .

Teknik hususlar

  • genellikle nesneleri karşılaştırmak (birkaç seviyede yineleme içerebilir) pahalıdır.
  • tercihen, hash()işlev bir büyüklük (veya birkaç) mertebesinde daha ucuzdur.
  • iki hash'i karşılaştırmak, iki nesneyi karşılaştırmaktan daha kolaydır, kısayolun olduğu yer burasıdır.

Sözlüklerin nasıl uygulandığını okursanız , karma tablolar kullanırlar; bu, bir nesneden bir anahtar türetmenin, sözlüklerdeki nesneleri almak için bir köşe taşı olduğu anlamına gelir O(1). Ancak bu, hash fonksiyonunuzun çarpışmaya dayanıklı olmasına çok bağlıdır . Bir öğeyi almak için en kötü durum bir sözlükte aslında O(n).

Bu notta, değişebilir nesneler genellikle hashable değildir. Hashable özelliği, bir nesneyi anahtar olarak kullanabileceğiniz anlamına gelir. Karma değeri bir anahtar olarak kullanılırsa ve aynı nesnenin içeriği değişirse, karma işlevi ne döndürmelidir? Aynı anahtar mı yoksa farklı mı? Bu bağlıdır Eğer karma işlevi nasıl tanımladığına.

Örnek olarak öğrenmek:

Bu sınıfa sahip olduğumuzu hayal edin:

>>> class Person(object):
...     def __init__(self, name, ssn, address):
...         self.name = name
...         self.ssn = ssn
...         self.address = address
...     def __hash__(self):
...         return hash(self.ssn)
...     def __eq__(self, other):
...         return self.ssn == other.ssn
... 

Lütfen dikkat: Bunların tümü, SSN'nin bir birey için asla değişmediği varsayımına dayanmaktadır (bu gerçeği yetkili kaynaktan gerçekten nerede doğrulayacağınızı bile bilmiyorum).

Ve bizde Bob var:

>>> bob = Person('bob', '1111-222-333', None)

Bob, ismini değiştirmek için bir yargıca gider:

>>> jim = Person('jim bo', '1111-222-333', 'sf bay area')

Bildiğimiz bu:

>>> bob == jim
True

Ancak bunlar, aynı kişinin iki farklı kaydı gibi, ayrılmış farklı belleğe sahip iki farklı nesnedir:

>>> bob is jim
False

Şimdi hash () işlevinin kullanışlı olduğu kısım geliyor:

>>> dmv_appointments = {}
>>> dmv_appointments[bob] = 'tomorrow'

Bil bakalım ne oldu:

>>> dmv_appointments[jim] #?
'tomorrow'

İki farklı kayıttan aynı bilgilere erişebilirsiniz. Şimdi şunu dene:

>>> dmv_appointments[hash(jim)]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 9, in __eq__
AttributeError: 'int' object has no attribute 'ssn'
>>> hash(jim) == hash(hash(jim))
True

Az önce ne oldu? Bu bir çarpışma. Çünkü hash(jim) == hash(hash(jim))hangi ikisi tamsayılar btw biz girişini karşılaştırmak gerekir şunlardır __getitem__çarpmasıyla, tüm öğeleri ile. Yerleşik intbir ssnniteliğe sahip değildir, bu nedenle tökezler.

>>> del Person.__eq__
>>> dmv_appointments[bob]
'tomorrow'
>>> dmv_appointments[jim]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: <__main__.Person object at 0x7f611bd37110>

Bu son örnekte, bir çarpışmada bile karşılaştırmanın yapıldığını, nesnelerin artık eşit olmadığını, yani başarılı bir şekilde a yükselttiğini gösteriyorum KeyError.


Gerçekten kullanışlı bir açıklama. Bir acemi olarak, bu, setlere yerleştirilebilecek sınıfların nasıl oluşturulacağını ve bunları sözlük / karma tablo için anahtar olarak nasıl kullanacağımı anlamama yardımcı oldu. Ayrıca [hashable_obj] = hashable_obj koleksiyonunu yaparsam, daha sonra bu örneğe bir işaretçi alabilirim. Ama bana bu tür koleksiyonları takip etmenin daha iyi bir yolu olup olmadığını söyle.
PaulDong

@dnozay Ama yine de, hash()çarpışmaya neden olabilen sabit boyutlu bir tam sayı
aşırı değişim

2
Birisi __eq__yukarıdaki örnekte kullanımını detaylandırabilir mi ? Sözlük tarafından aldığı anahtarla sahip olduğu tüm anahtarları karşılaştırmaya çalışırken mi çağrılır? Böyle tarafından bu son örnekte yöntemle, sözlük buna sahip tuşlarıyla aldığı anahtarla denkliğini tespit için kullanılacak çağrı yok yani? del__eq__
Jet Blue

1
@JetBlue "Collosion" açıklaması, anahtarlı örnekte eksiktir hash(jim). Person.__eq__çağrılır çünkü mevcut anahtar, hash(jim)bu doğru anahtarın Person.__eq__kullanıldığından emin olmak için aynı karmaya sahiptir . O varsayar çünkü errs otherolan intbir sahiptir ssnözelliği. Eğer hash(jim)anahtar sözlükte yoktu __eq__çağrılmayacaktı. Bu, anahtar aramasının ne zaman O (n) olabileceğini açıklar: tüm öğeler aynı karmaya sahip olduğunda __eq__, örneğin anahtarın olmadığı durumlarda, hepsinde kullanılması gerekir.
WloHu

1
Örneğinizin pedagojik ilgisini anlasam da dmv_appointments[bob.ssn] = 'tomorrow', bir __hash__yöntem tanımlama ihtiyacını ortadan kaldırarak sadece yazmak daha kolay olmaz mıydı ? Yazıp okuduğunuz her randevuya 4 karakter eklediğini anlıyorum, ama bana daha net geliyor.
Alexis

3

Durum için Python belgelerihash() :

Hash değerleri tamsayılardır. Sözlük araması sırasında sözlük tuşlarını hızlı bir şekilde karşılaştırmak için kullanılırlar.

Python sözlükleri karma tablolar olarak uygulanır. Yani bir sözlük kullandığınızda, hash()atama veya arama için verdiğiniz tuşlar çağrılır.

Ek olarak, tür durumu için belgelerdict :

Olmayan değerler hashable olduğu, listeler, sözlük veya anahtar olarak kullanılamaz (değeriyle yerine nesne kimliği ile karşılaştırıldığında), diğer değişken türlerini içeren değerleri.


1

Hash, sözlükler tarafından kullanılır ve nesneyi hızlı bir şekilde aramak için ayarlar. İyi bir başlangıç ​​noktası, Wikipedia'nın hash tabloları hakkındaki makalesidir .


-2

Sen kullanabilirsiniz DictionaryPython veri türü. Hash'e çok benzer ve aynı zamanda iç içe geçmiş hash'e benzer şekilde yerleştirmeyi de destekler.

Misal:

dict = {'Name': 'Zara', 'Age': 7, 'Class': 'First'}
dict['Age'] = 8; # update existing entry
dict['School'] = "DPS School" # Add new entry

print ("dict['Age']: ", dict['Age'])
print ("dict['School']: ", dict['School'])

Daha fazla bilgi için lütfen sözlük veri türü ile ilgili bu eğiticiye başvurun .

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.