ハッシュはPythonで何をしますか?


88

hash関数がタプルに適用されるコードの例を見ました。結果として、負の整数を返します。この関数は何をするのだろうか?グーグルは助けません。ハッシュの計算方法を説明しているページを見つけましたが、なぜこの関数が必要なのか説明していません。


8
あなたは見ていました...ドキュメント
TerryA

このリンク(公式ドキュメント)にアクセスしてください。それはすべてを指定します。行くのリンク
tailor_raj 2013

2
質問が「それは何ですか」ではなく「なぜそれが必要なのか」の繰り返しであることが好きです。
dnozay 2013

公式リンクは非常に紛らわしいです
Rasmi RanjanNayak20年

回答:


156

ハッシュは、特定の値を識別する固定サイズの整数です。各値には独自のハッシュが必要であるため、同じオブジェクトでなくても、同じ値に対して同じハッシュを取得します。

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

ハッシュ値は、結果の値が均等に分散されるように作成して、発生するハッシュ衝突の数を減らす必要があります。ハッシュの衝突は、2つの異なる値が同じハッシュを持つ場合です。したがって、比較的小さな変更では、ハッシュが大きく異なることがよくあります。

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

これらの数値は、値の大規模なコレクション内の値をすばやく検索できるため、非常に便利です。それらの使用例の2つは、Pythonsetdictです。でlist、値がリストにあるかどうかを確認する場合は、でif x in values:、Pythonはリスト全体を調べxて、リスト内の各値と比較する必要がありますvalues。これには長い時間がかかる場合がありlistます。でset、Pythonは各ハッシュを追跡します。入力するとif x in values:、Pythonはのハッシュ値を取得xし、内部構造でそれを検索xして、と同じハッシュを持つ値とのみ比較しますx

同じ方法が辞書検索に使用されます。これは、ルックアップになりsetdict、ルックアップでは、一方で、非常に速いlist遅いです。またlist、ハッシュできないオブジェクトを、に含めることはできますが、setまたはのキーとして使用することはできませんdict。ハッシュ不可能なオブジェクトの典型的な例は、変更可能なオブジェクトです。つまり、その値を変更できます。可変オブジェクトがある場合、そのハッシュはその存続期間中に変化するため、ハッシュ可能であってはなりません。オブジェクトが辞書内の間違ったハッシュ値の下に置かれる可能性があるため、多くの混乱を引き起こします。

値のハッシュは、Pythonの1回の実行で同じである必要があるだけであることに注意してください。Python 3.3では、実際には、Pythonを新しく実行するたびに変更されます。

$ /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

これは、特定の文字列がどのハッシュ値を持つかを推測しにくくするためです。これは、Webアプリケーションなどの重要なセキュリティ機能です。

したがって、ハッシュ値を永続的に保存しないでください。永続的な方法でハッシュ値を使用する必要がある場合は、ファイルなどの検証可能なチェックサムを作成するために使用できる、より「深刻な」タイプのハッシュ、暗号化ハッシュ関数を調べることができます。


12
潜在的なハッシュ衝突について:hash(-1) == hash(-2)(Python 2.7を実行)
Matthias

2
Python 3.6.1を実行していますが、衝突が発生しています。
The_Martian

hash(-1) == hash(-2)今日でも存在します。幸い、辞書やセットのルックアップに悪影響を与えることはありません。ihash(i)除いて、他のすべての整数はそれ自体に解決され-1ます。
クリス・コンラン

36

TL; DR:

用語集を参照しください:hash()オブジェクトを比較するためのショートカットとして使用されます。他のオブジェクトと比較できる場合、そのオブジェクトはハッシュ可能と見なされます。そのため、を使用しますhash()。また、アクセスするために使用されますdictsetとして実装されている要素はCPythonでサイズ変更可能なハッシュテーブル

技術的な考慮事項

  • 通常、オブジェクトの比較(複数レベルの再帰が含まれる場合があります)にはコストがかかります。
  • 好ましくは、hash()関数は一桁(または数桁)安価である。
  • 2つのハッシュを比較することは、2つのオブジェクトを比較するよりも簡単です。ここに、ショートカットがあります。

ディクショナリの実装方法について読むと、ハッシュテーブルが使用されます。つまり、オブジェクトからキーを取得することは、のディクショナリ内のオブジェクトを取得するための礎石ですO(1)。ただし、衝突耐性があるかどうかはハッシュ関数に大きく依存します。辞書に項目を取得するため最悪のケースは、実際にはO(n)です。

その点で、可変オブジェクトは通常ハッシュ可能ではありません。ハッシュ可能なプロパティは、オブジェクトをキーとして使用できることを意味します。ハッシュ値がキーとして使用され、同じオブジェクトの内容が変更された場合、ハッシュ関数は何を返す必要がありますか?同じキーですか、それとも別のキーですか?それは異なり、あなたのハッシュ関数を定義する方法に。

例による学習:

このクラスがあると想像してください。

>>> 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
... 

注意:これはすべて、SSNが個人に対して変更されないという仮定に基づいています(信頼できる情報源からその事実を実際にどこで確認するかさえわかりません)。

そして、ボブがいます:

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

ボブは彼の名前を変えるために裁判官に会いに行きます:

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

これは私たちが知っていることです:

>>> bob == jim
True

しかし、これらは、同じ人物の2つの異なるレコードのように、異なるメモリが割り当てられた2つの異なるオブジェクトです。

>>> bob is jim
False

次に、hash()が便利な部分があります。

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

何だと思う:

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

2つの異なるレコードから、同じ情報にアクセスできます。今これを試してください:

>>> 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

今何があったの?それは衝突です。hash(jim) == hash(hash(jim))どちらも整数であるため、の入力__getitem__を衝突するすべてのアイテムと比較する必要があります。ビルトインにintssn属性がないため、トリップします。

>>> 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>

この最後の例では、衝突があっても比較が実行され、オブジェクトが等しくなくなったことを示していKeyErrorます。これは、が正常に発生することを意味します。


本当に便利な説明。初心者として、これは、セットに入れることができるクラスを作成し、それらを辞書/ハッシュテーブルのキーとして使用する方法を理解するのに役立ちました。また、collection [hashable_obj] = hashable_objを実行すると、後でそのインスタンスへのポインターを取得できます。しかし、そのようなコレクションを追跡するためのより良い方法があるかどうか教えてください。
PaulDong 2017年

@dnozayしかし、それでも、の出力hash()は固定サイズの整数であり、衝突を引き起こす可能性があります
オーバーエクスチェンジ2017年

2
__eq__上記の例での使用について誰かが詳しく説明できますか?受け取ったキーを持っているすべてのキーと比較しようとしているときに、辞書によって呼び出されますか?で、このような最後の例では方法、辞書は、それが持っているキーで受信したキーの等価性を判断するために使用するために呼び出すには何もしていますか?del__eq__
ジェットブルー

1
@JetBlue「collosion」の説明は、keyを使用した例では不完全hash(jim)です。Person.__eq__既存のキーには、hash(jim)これが正しいキーPerson.__eq__が使用されることを保証するためと同じハッシュがあるため、が呼び出されます。other、であるが属性をint持っていると想定しているため、エラーが発生しssnます。場合はhash(jim)、キーが辞書に存在しなかった__eq__と呼ばれることはありません。これは、キールックアップがO(n)になる可能性がある場合を説明します__eq__。たとえば、キーが存在しない場合など、すべてのアイテムが同じハッシュを持つ場合は、すべてのアイテムで使用する必要があります。
wloHu 2018

1
私はあなたの例の教育学的関心を理解しdmv_appointments[bob.ssn] = 'tomorrow'ていますが、__hash__メソッドを定義する必要をなくして、書くだけの方が簡単ではないでしょうか?読み書きする予定ごとに4文字が追加されることは理解していますが、私にはわかりやすいようです。
アレクシス

3

状態に関するPythonドキュメントhash()

ハッシュ値は整数です。これらは、辞書検索中に辞書キーをすばやく比較するために使用されます。

Python辞書はハッシュテーブルとして実装されています。したがって、辞書を使用するときはいつでもhash()、割り当てまたはルックアップのために渡すキーで呼び出されます。

さらに、タイプ状態のドキュメントdict

ハッシュ可能ではない値、つまり、リスト、辞書、またはその他の変更可能なタイプ(オブジェクトIDではなく値によって比較される)を含む値は、キーとして使用できません。


1

ハッシュは、オブジェクトをすばやく検索するために辞書とセットによって使用されます。良い出発点は、ハッシュテーブルに関するウィキペディアの記事です。


-2

DictionaryPythonでデータ型を使用できます。ハッシュと非常によく似ています。また、ネストされたハッシュと同様に、ネストもサポートしています。

例:

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'])

詳細については、辞書のデータ型に関するこのチュートリアルを参照してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.