web-dev-qa-db-ja.com

pythonで適切な__hash__関数を実装する方法

複数のプロパティを持つクラスを実装する場合(以下のおもちゃの例のように)、ハッシュを処理する最良の方法は何ですか?

__eq__および__hash__は一貫している必要がありますが、すべてのプロパティを処理できる適切なハッシュ関数を実装する方法は?

class AClass:
  def __init__(self):
      self.a = None
      self.b = None

  def __eq__(self, other):
      return other and self.a == other.a and self.b == other.b

  def __ne__(self, other):
    return not self.__eq__(other)

  def __hash__(self):
      return hash((self.a, self.b))

この質問 を読んで、タプルはハッシュ可能であるため、上記の例のようなものが賢明であるかどうか疑問に思っていました。それは...ですか?

91
abahgat

__hash__は、等しいオブジェクトに対して同じ値を返す必要があります。また、オブジェクトの存続期間にわたって変化してはなりません。通常、不変オブジェクトに対してのみ実装します。

取るに足らない実装は、ただreturn 0になります。これは常に正しいものですが、パフォーマンスが低下します。

Tuple of propertiesのハッシュを返すソリューションは適切です。ただし、タプルの__eq__で比較するすべてのプロパティをリストする必要はありません。通常、一部のプロパティが不等式オブジェクトに対して同じ値を持っている場合は、そのままにしておきます。ハッシュ計算を必要以上に高価にしないでください。

編集:一般的にハッシュを混ぜるためにxorを使用することをお勧めします。 2つの異なるプロパティの値が同じ場合、それらのハッシュは同じになり、xorを使用すると、これらは互いにキャンセルされます。タプルは、より複雑な計算を使用してハッシュを混合します。 tupleobject.ctuplehashを参照してください。

69
adw

object.__hash__(self) のドキュメント

唯一必要なプロパティは、等しいと比較するオブジェクトが同じハッシュ値を持つことです。オブジェクトの比較においても役割を果たすオブジェクトのコンポーネントのハッシュ値を何らかの方法で混合することをお勧めします(たとえば、排他的ORを使用するなど)。

def __hash__(self):
    return hash(self.a) ^ hash(self.b)
13
S.Lott

書くのは危険です

def __eq__(self, other):
  return other and self.a == other.a and self.b == other.b

rhs(つまり、other)オブジェクトがブール値のFalseに評価される場合、それは何とも等しいと決して比較されないためです!

さらに、otherAClassのクラスまたはサブクラスに属しているかどうかを再確認することもできます。そうでない場合は、例外AttributeErrorまたはfalse positiveが発生します(他のクラスに、一致する値を持つ同じ名前の属性がある場合)。したがって、__eq__ as:

def __eq__(self, other):
  return isinstance(other, self.__class__) and self.a == other.a and self.b == other.b

万が一、属性が名前で一致する限り無関係なクラス間で比較する、非常に柔軟な比較が必要な場合は、少なくともAttributeErrorを避け、otherが一致しないことを確認する必要があります。 tには追加の属性があります。方法は状況によって異なります(オブジェクトのすべての属性を見つける標準的な方法はないため)。

12
max