hash
関数がタプルに適用されるコードの例を見ました。結果として、負の整数を返します。この関数は何をするのだろうか?グーグルは助けません。ハッシュの計算方法を説明しているページを見つけましたが、なぜこの関数が必要なのか説明していません。
hash
関数がタプルに適用されるコードの例を見ました。結果として、負の整数を返します。この関数は何をするのだろうか?グーグルは助けません。ハッシュの計算方法を説明しているページを見つけましたが、なぜこの関数が必要なのか説明していません。
回答:
ハッシュは、特定の値を識別する固定サイズの整数です。各値には独自のハッシュが必要であるため、同じオブジェクトでなくても、同じ値に対して同じハッシュを取得します。
>>> hash("Look at me!")
4343814758193556824
>>> f = "Look at me!"
>>> hash(f)
4343814758193556824
ハッシュ値は、結果の値が均等に分散されるように作成して、発生するハッシュ衝突の数を減らす必要があります。ハッシュの衝突は、2つの異なる値が同じハッシュを持つ場合です。したがって、比較的小さな変更では、ハッシュが大きく異なることがよくあります。
>>> hash("Look at me!!")
6941904779894686356
これらの数値は、値の大規模なコレクション内の値をすばやく検索できるため、非常に便利です。それらの使用例の2つは、Pythonset
とdict
です。でlist
、値がリストにあるかどうかを確認する場合は、でif x in values:
、Pythonはリスト全体を調べx
て、リスト内の各値と比較する必要がありますvalues
。これには長い時間がかかる場合がありlist
ます。でset
、Pythonは各ハッシュを追跡します。入力するとif x in values:
、Pythonはのハッシュ値を取得x
し、内部構造でそれを検索x
して、と同じハッシュを持つ値とのみ比較しますx
。
同じ方法が辞書検索に使用されます。これは、ルックアップになりset
とdict
、ルックアップでは、一方で、非常に速い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アプリケーションなどの重要なセキュリティ機能です。
したがって、ハッシュ値を永続的に保存しないでください。永続的な方法でハッシュ値を使用する必要がある場合は、ファイルなどの検証可能なチェックサムを作成するために使用できる、より「深刻な」タイプのハッシュ、暗号化ハッシュ関数を調べることができます。
hash(-1) == hash(-2)
(Python 2.7を実行)
hash(-1) == hash(-2)
今日でも存在します。幸い、辞書やセットのルックアップに悪影響を与えることはありません。i
をhash(i)
除いて、他のすべての整数はそれ自体に解決され-1
ます。
用語集を参照してください:hash()
オブジェクトを比較するためのショートカットとして使用されます。他のオブジェクトと比較できる場合、そのオブジェクトはハッシュ可能と見なされます。そのため、を使用しますhash()
。また、アクセスするために使用されますdict
とset
として実装されている要素はCPythonでサイズ変更可能なハッシュテーブル。
hash()
関数は一桁(または数桁)安価である。ディクショナリの実装方法について読むと、ハッシュテーブルが使用されます。つまり、オブジェクトからキーを取得することは、のディクショナリ内のオブジェクトを取得するための礎石です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__
を衝突するすべてのアイテムと比較する必要があります。ビルトインにint
はssn
属性がないため、トリップします。
>>> 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
ます。これは、が正常に発生することを意味します。
hash()
は固定サイズの整数であり、衝突を引き起こす可能性があります
__eq__
上記の例での使用について誰かが詳しく説明できますか?受け取ったキーを持っているすべてのキーと比較しようとしているときに、辞書によって呼び出されますか?で、このような最後の例では方法、辞書は、それが持っているキーで受信したキーの等価性を判断するために使用するために呼び出すには何もしていますか?del
__eq__
hash(jim)
です。Person.__eq__
既存のキーには、hash(jim)
これが正しいキーPerson.__eq__
が使用されることを保証するためと同じハッシュがあるため、が呼び出されます。other
、であるが属性をint
持っていると想定しているため、エラーが発生しssn
ます。場合はhash(jim)
、キーが辞書に存在しなかった__eq__
と呼ばれることはありません。これは、キールックアップがO(n)になる可能性がある場合を説明します__eq__
。たとえば、キーが存在しない場合など、すべてのアイテムが同じハッシュを持つ場合は、すべてのアイテムで使用する必要があります。
dmv_appointments[bob.ssn] = 'tomorrow'
ていますが、__hash__
メソッドを定義する必要をなくして、書くだけの方が簡単ではないでしょうか?読み書きする予定ごとに4文字が追加されることは理解していますが、私にはわかりやすいようです。
状態に関するPythonドキュメントhash()
:
ハッシュ値は整数です。これらは、辞書検索中に辞書キーをすばやく比較するために使用されます。
Python辞書はハッシュテーブルとして実装されています。したがって、辞書を使用するときはいつでもhash()
、割り当てまたはルックアップのために渡すキーで呼び出されます。
さらに、タイプ状態のドキュメントdict
:
ハッシュ可能ではない値、つまり、リスト、辞書、またはその他の変更可能なタイプ(オブジェクトIDではなく値によって比較される)を含む値は、キーとして使用できません。
Dictionary
Pythonでデータ型を使用できます。ハッシュと非常によく似ています。また、ネストされたハッシュと同様に、ネストもサポートしています。
例:
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'])
詳細については、辞書のデータ型に関するこのチュートリアルを参照してください。