[python] ハッシュ可能、不変


Answers

技術的には、ハッシュ可能とは、クラスが__hash __()を定義することを意味します。 ドキュメントによると:

__hash __()は整数を返さなければなりません。 唯一必要なプロパティは、equalを比較するオブジェクトが同じハッシュ値を持つことです。 何らかの形で、(例えば、排他的またはを使用して)オブジェクトの構成要素のハッシュ値を混在させ、オブジェクトの比較において部分的に再生することが推奨される。

私は、Pythonの組み込み型では、すべてのハッシュ可能な型も不変であると思います。 それにもかかわらず、__hash __()を定義する可変オブジェクトを持つことは困難ですが、おそらく不可能ではありません。

Question

最近のSOの質問( リストで索引付けされているPythonで辞書作成するを参照)から、私はおそらく、Pythonでハッシュ可能で不変なオブジェクトの意味について間違った概念があることに気付きました。

  • 実際にハッシュ可能な意味は何ですか?
  • ハッシュ可能と不可能の関係は何ですか?
  • ハッシュ可能な可変オブジェクトや、ハッシュ可能でない不変オブジェクトがありますか?



Pythonでは、ほとんど互換性があります。 ハッシュは内容を表現することになっているので、オブジェクトと同じように変更可能であり、オブジェクトがハッシュ値を変更すると、dictキーとして使用できなくなります。

他の言語では、ハッシュ値はオブジェクトの「アイデンティティ」に関連しており、必ずしもその値には関係しません。 したがって、可変オブジェクトの場合、ポインタを使用してハッシングを開始することができます。 もちろん、オブジェクトがメモリ内で移動しないと仮定すると(GCのように)。 これは、例えばLuaで使用されるアプローチです。 これは、可変オブジェクトをテーブルキーとして使用可能にします。 初心者のためにいくつかの(不愉快な)驚きを作り出します。

最終的に、不変なシーケンス型(タプル)を持つことは、 '多値キー'にとってより良いものになります。




変更不可能とは、オブジェクトが存続期間中に重要な形で変化しないことを意味します。 プログラミング言語ではあいまいだが一般的な考えです。

ハッシュ能力はわずかに異なり、比較を指しています。

ハッシュ可能オブジェクトは、生存期間中に変更されない__eq__() __cmp__()メソッドが必要です)ハッシュ値を持ち、他のオブジェクトと比較できます( __cmp__()メソッドまたは__cmp__()メソッドが必要)。 等しいを比較するハッシュ可能オブジェクトは、同じハッシュ値を持たなければなりません。

すべてのユーザー定義クラスには__hash__メソッドがあり、デフォルトではオブジェクトIDだけが返されます。 したがって、ハッシュ可能性の基準を満たすオブジェクトは必ずしも不変ではありません。

あなたが宣言した新しいクラスのオブジェクトは、たとえば__hash__から__hash__などしない限り、辞書キーとして使うことができます

オブジェクトの存続期間中にハッシュが変更された場合、オブジェクトが変更されたことを意味するため、すべての不変オブジェクトはハッシュ可能であると言うことができます。

しかし、それほどではない。 リスト(変更可能)を持つタプルを考えてみましょう。 タプルは不変であると言う人もいますが、同時にハッシュ可能ではありません(スロー)。

d = dict()
d[ (0,0) ] = 1    #perfectly fine
d[ (0,[0]) ] = 1  #throws

ハッシュ能力と不変性は、タイプではなくオブジェクトのインスタンスを参照します。 例えば、タプル型のオブジェクトは、ハッシュ可能であるかどうかを問わない。




相互作用のために不変とハッシュ可能との間に強制される明示的な関係がない場合でも暗黙的に存在する

  1. 等しいかどうかを比較するハッシュ可能オブジェクトは、同じハッシュ値を持つ必要があります
  2. オブジェクトは、その存続期間中に決して変化しないハッシュ値を有する場合、ハッシュ可能である。

__eq__を再定義しない限り、オブジェクトクラスは値の等価性を定義しない限り、ここで問題はありません。

これを行うと、同じ値を表すオブジェクト(たとえば、 __eq__どこ__eq__ )がTrueを返し、オブジェクトの存続期間中は決して変化しない安定したハッシュ関数を見つける必要があります。

これが可能な場合はアプリケーションを見るのが難しいので、これらの要件を満たす可能性のあるクラスAを検討してください。 __hash__が定数を返すという明らかな退化的なケースが__hash__ますが。

今すぐ: -

>>> a = A(1)
>>> b = A(1)
>>> c = A(2)
>>> a == b
True
>>> a == c
False
>>> hash(a) == hash(b)
True
>>> a.set_value(c)
>>> a == c
True
>>> assert(hash(a) == hash(c)) # Because a == c => hash(a) == hash(c)
>>> assert(hash(a) == hash(b)) # Because hash(a) and hash(b) have compared equal 
                                 before and the result must stay static over the objects lifetime.

実際、これは生成ハッシュ(b)==ハッシュ(c)を意味します。 私は値で比較する定義可能な変更可能なオブジェクトのために__hash__ ()を便利に定義するために、とにかく見ることに苦労します。

__lt____le____gt__ 、および__ge__コンパイルは影響を受けません。 __lt____le__可能なオブジェクトの順序付けを定義できます。




Links