背景:
Pythonでは、オブジェクトを値で比較する際、演算子 '==' が使われ、内部でメソッド eq が呼び出されます。このメソッドのデフォルトの動作は、object から継承され、オブジェクトの同一性を比較します(ほとんどのクラスで)。
問題:
ユーザー定義のクラスのインスタンスを意味のある方法で比較したい場合(例えば、同じデータセットを持つ異なる Person オブジェクトが等しいとみなされる場合)、標準の動作では不十分です — 比較がどのように行われるかを明示的に定義する必要があります。
解決策:
'==' の動作を変更するには、自分のクラス内で eq メソッドをオーバーライドする必要があります。クラスがハッシュ可能である必要がある場合、hash もオーバーライドする必要があります。
コードの例:
class Person: def __init__(self, name, age): self.name = name self.age = age def __eq__(self, other): if not isinstance(other, Person): return NotImplemented return self.name == other.name and self.age == other.age p1 = Person("Ann", 25) p2 = Person("Ann", 25) print(p1 == p2) # True
主な特徴:
異なる型のオブジェクトに対して、'==' の比較は常に対称的ですか?
必ずしもそうではありません — 最初のオブジェクトが NotImplemented を返すと、逆の比較が呼び出されます。そうでなければ、非対称になる可能性があります。
class A: def __eq__(self, other): return True class B: pass print(A() == B()) # True print(B() == A()) # False
eq を実装しない場合、同じユーザー定義クラスのオブジェクトはどう比較されますか?
同一性(メモリ内の同一性、属性の値ではなく)が比較されます。
class Foo: def __init__(self, x): self.x = x f1 = Foo(5) f2 = Foo(5) print(f1 == f2) # False
辞書のキーであるべきオブジェクトに対して eq を hash なしで定義できますか?
いいえ。もし eq が変更された場合、hash も定義することが望ましく、そうしないとオブジェクトはハッシュ可能でなくなります(キーとして使用すると TypeError が発生します)。
class Foo: def __eq__(self, other): return True # hash は object から継承されますが、動作は予測不可能になります # 明示的に __hash__ を定義する方が良いです
ネガティブケース
ValueObject クラスの設計時に、eq のみを実装し、hash を実装し忘れました。オブジェクトを辞書のキーまたはセットの要素として使用しようとしたときに、TypeError が発生しました。
利点:
ユーザー定義の比較を迅速に導入できます。
欠点:
オブジェクトをセットや辞書で使用できない、デバッグが難しい。
ポジティブケース
別のプロジェクトでは、開発者が明示的に両方のメソッド — eq と hash を実装し、「2つのオブジェクトが eq で等しいならば、ハッシュも一致する」というルールに厳密に従いました。
利点:
ハッシュ構造が正しく機能し、一貫した動作、メンテナンスが容易です。
欠点:
値が等しいオブジェクトがハッシュコンテナ内で識別できないため、柔軟性の喪失。