web-dev-qa-db-ja.com

hash()とid()の違い

abの2つのユーザー定義オブジェクトがあります。
これらのオブジェクトは両方とも同じhash値を持っています。
ただし、id(a)id(b)は等しくありません。

また、

>>> a is b
False
>>> a == b
True

この観察から、私は次のことを推測できますか?

  • 等しくないオブジェクトは、同じhash値を持つ可能性があります。
  • 等しいオブジェクトは、同じid値を持つ必要があります。
  • いつでもobj1 is obj2が呼び出されると、両方のオブジェクトのid値が比較され、それらのhash値は比較されません。
22
Kshitij Saraogi

idhash、および==およびis演算子を理解しようとするときに理解する必要のある3つの概念があります。identityvalueおよびハッシュ値。すべてのオブジェクトに3つすべてがあるわけではありません。

  1. すべてのオブジェクトにはidentityがありますが、これでも少し滑りやすい場合があります。 id関数は、オブジェクトのIDに対応する番号を返します(cpythonでは、オブジェクトのメモリアドレスを返しますが、他のインタープリターは別のものを返す場合があります)。 2つのオブジェクト(同時に存在する)が同じIDを持っている場合、それらは実際には同じオブジェクトへの2つの参照です。 is演算子は、IDによってアイテムを比較します。a is bid(a) == id(b)と同等です。

    実装のどこかにキャッシュされているオブジェクトを処理する場合、IDは少し混乱する可能性があります。たとえば、cpythonの小さな整数と文字列のオブジェクトは、使用されるたびに再作成されるわけではありません。代わりに、既存のオブジェクトは必要なときにいつでも返されます。ただし、これはcpythonの実装の詳細であるため、コードでこれに依存しないでください(他のインタープリターは異なる方法で行うか、まったく行わない場合があります)。

  2. すべてのオブジェクトにもvalueがありますが、これは少し複雑です。一部のオブジェクトには、ID以外に意味のある値がありません(したがって、IDの値は、場合によっては同義語である可能性があります)。値は==演算子が比較するものとして定義できるため、a == bの場合はいつでも、abの値は同じであると言えます。コンテナオブジェクト(リストなど)には、その内容によって定義される値がありますが、他の種類のオブジェクトには、その属性に基づいた値があります。異なるタイプのオブジェクトは、数値の場合と同様に、同じ値を持つ場合があります:0 == 0.0 == 0j == decimal.Decimal("0") == fractions.Fraction(0) == False(はい、歴史的な理由から、boolsはPythonの数値です)。

    クラスが(__eq__演算子を実装するために)==メソッドを定義しない場合、クラスはobjectからデフォルトバージョンを継承し、そのインスタンスはIDによってのみ比較されます。これは、他の点では同一のインスタンスに重要なセマンティックの違いがある場合に適しています。たとえば、同じホストの同じポートに接続されている2つの異なるソケットは、一方がHTML Webページをフェッチし、もう一方がそのページからリンクされた画像を取得している場合、異なる方法で処理する必要があるため、同じ値を持ちません。

  3. 値に加えて、一部のオブジェクトにはハッシュ値があります。これは、それらを辞書キーとして使用できる(およびsetsに格納できる)ことを意味します。関数hash(a)は、オブジェクトaのハッシュ値(オブジェクトの値に基づく数値)を返します。オブジェクトのハッシュは、オブジェクトの存続期間中同じである必要があるため、オブジェクトの値が不変である場合にのみ、オブジェクトがハッシュ可能であることが意味をなします(オブジェクトのIDに基づいているか、オブジェクトのコンテンツに基づいているため)。それ自体が不変であるオブジェクト)。

    複数の異なるオブジェクトが同じハッシュ値を持つ場合がありますが、適切に設計されたハッシュ関数はこれを可能な限り回避します。同じハッシュを持つオブジェクトをディクショナリに格納することは、異なるハッシュを持つオブジェクトを格納するよりもはるかに効率的ではありません(ハッシュの衝突ごとに多くの作業が必要になります)。オブジェクトはデフォルトでハッシュ可能です(デフォルト値はIDであり、不変であるため)。カスタムクラスで__eq__メソッドを作成する場合、Pythonは、このデフォルトのハッシュ実装を無効にします。これは、__eq__関数がそのインスタンスの値の新しい意味を定義するためです。クラスを引き続きハッシュ可能にする場合は、__hash__メソッドも使用できます。ハッシュ可能クラスから継承しているが、自分でハッシュ可能にしたくない場合は、クラス本体に__hash__ = Noneを設定できます。

37
Blckknght

等しくないオブジェクトは同じハッシュ値を持つ可能性があります。

はい、その通りです。簡単な例は、CPythonのhash(-1) == hash(-2)です。

等しいオブジェクトは同じID値を持つ必要があります。

いいえ、これは一般的に誤りです。 @chepnerが指摘した単純な反例は、_5 == 5.0_ですがid(5) != id(5.0)です。

_obj1 is obj2_が呼び出されるたびに、ハッシュ値ではなく、両方のオブジェクトのID値が比較されます。

はい、その通りです。 isは、オブジェクトのidが等しいかどうかを比較します(CPythonでは、オブジェクトのメモリアドレスです)。一般に、これはオブジェクトのハッシュ値とは何の関係もありません(オブジェクトはハッシュ可能である必要さえありません)。

6
Alex Riley

ハッシュ関数は次の目的で使用されます。

辞書検索中に辞書キーをすばやく比較する

iD関数は次の目的で使用されます。

オブジェクトの「アイデンティティ」を返します。これは、その存続期間中、このオブジェクトに対して一意で一定であることが保証されている整数です。ライフタイムが重複しない2つのオブジェクトは、同じid()値を持つ場合があります。

1
Ryan