a
とb
の2つのユーザー定義オブジェクトがあります。
これらのオブジェクトは両方とも同じhash
値を持っています。
ただし、id(a)
とid(b)
は等しくありません。
また、
>>> a is b
False
>>> a == b
True
この観察から、私は次のことを推測できますか?
hash
値を持つ可能性があります。id
値を持つ必要があります。obj1 is obj2
が呼び出されると、両方のオブジェクトのid
値が比較され、それらのhash
値は比較されません。id
、hash
、および==
およびis
演算子を理解しようとするときに理解する必要のある3つの概念があります。identity、valueおよびハッシュ値。すべてのオブジェクトに3つすべてがあるわけではありません。
すべてのオブジェクトにはidentityがありますが、これでも少し滑りやすい場合があります。 id
関数は、オブジェクトのIDに対応する番号を返します(cpythonでは、オブジェクトのメモリアドレスを返しますが、他のインタープリターは別のものを返す場合があります)。 2つのオブジェクト(同時に存在する)が同じIDを持っている場合、それらは実際には同じオブジェクトへの2つの参照です。 is
演算子は、IDによってアイテムを比較します。a is b
はid(a) == id(b)
と同等です。
実装のどこかにキャッシュされているオブジェクトを処理する場合、IDは少し混乱する可能性があります。たとえば、cpythonの小さな整数と文字列のオブジェクトは、使用されるたびに再作成されるわけではありません。代わりに、既存のオブジェクトは必要なときにいつでも返されます。ただし、これはcpythonの実装の詳細であるため、コードでこれに依存しないでください(他のインタープリターは異なる方法で行うか、まったく行わない場合があります)。
すべてのオブジェクトにもvalueがありますが、これは少し複雑です。一部のオブジェクトには、ID以外に意味のある値がありません(したがって、IDの値は、場合によっては同義語である可能性があります)。値は==
演算子が比較するものとして定義できるため、a == b
の場合はいつでも、a
とb
の値は同じであると言えます。コンテナオブジェクト(リストなど)には、その内容によって定義される値がありますが、他の種類のオブジェクトには、その属性に基づいた値があります。異なるタイプのオブジェクトは、数値の場合と同様に、同じ値を持つ場合があります:0 == 0.0 == 0j == decimal.Decimal("0") == fractions.Fraction(0) == False
(はい、歴史的な理由から、bool
sはPythonの数値です)。
クラスが(__eq__
演算子を実装するために)==
メソッドを定義しない場合、クラスはobject
からデフォルトバージョンを継承し、そのインスタンスはIDによってのみ比較されます。これは、他の点では同一のインスタンスに重要なセマンティックの違いがある場合に適しています。たとえば、同じホストの同じポートに接続されている2つの異なるソケットは、一方がHTML Webページをフェッチし、もう一方がそのページからリンクされた画像を取得している場合、異なる方法で処理する必要があるため、同じ値を持ちません。
値に加えて、一部のオブジェクトにはハッシュ値があります。これは、それらを辞書キーとして使用できる(およびset
sに格納できる)ことを意味します。関数hash(a)
は、オブジェクトa
のハッシュ値(オブジェクトの値に基づく数値)を返します。オブジェクトのハッシュは、オブジェクトの存続期間中同じである必要があるため、オブジェクトの値が不変である場合にのみ、オブジェクトがハッシュ可能であることが意味をなします(オブジェクトのIDに基づいているか、オブジェクトのコンテンツに基づいているため)。それ自体が不変であるオブジェクト)。
複数の異なるオブジェクトが同じハッシュ値を持つ場合がありますが、適切に設計されたハッシュ関数はこれを可能な限り回避します。同じハッシュを持つオブジェクトをディクショナリに格納することは、異なるハッシュを持つオブジェクトを格納するよりもはるかに効率的ではありません(ハッシュの衝突ごとに多くの作業が必要になります)。オブジェクトはデフォルトでハッシュ可能です(デフォルト値はIDであり、不変であるため)。カスタムクラスで__eq__
メソッドを作成する場合、Pythonは、このデフォルトのハッシュ実装を無効にします。これは、__eq__
関数がそのインスタンスの値の新しい意味を定義するためです。クラスを引き続きハッシュ可能にする場合は、__hash__
メソッドも使用できます。ハッシュ可能クラスから継承しているが、自分でハッシュ可能にしたくない場合は、クラス本体に__hash__ = None
を設定できます。
等しくないオブジェクトは同じハッシュ値を持つ可能性があります。
はい、その通りです。簡単な例は、CPythonのhash(-1) == hash(-2)
です。
等しいオブジェクトは同じID値を持つ必要があります。
いいえ、これは一般的に誤りです。 @chepnerが指摘した単純な反例は、_5 == 5.0
_ですがid(5) != id(5.0)
です。
_
obj1 is obj2
_が呼び出されるたびに、ハッシュ値ではなく、両方のオブジェクトのID値が比較されます。
はい、その通りです。 is
は、オブジェクトのid
が等しいかどうかを比較します(CPythonでは、オブジェクトのメモリアドレスです)。一般に、これはオブジェクトのハッシュ値とは何の関係もありません(オブジェクトはハッシュ可能である必要さえありません)。
ハッシュ関数は次の目的で使用されます。
辞書検索中に辞書キーをすばやく比較する
iD関数は次の目的で使用されます。
オブジェクトの「アイデンティティ」を返します。これは、その存続期間中、このオブジェクトに対して一意で一定であることが保証されている整数です。ライフタイムが重複しない2つのオブジェクトは、同じid()値を持つ場合があります。