web-dev-qa-db-ja.com

Python-クラス__hash__メソッドとセット

pythonクラスのset()および___hash___メソッドを使用して、同じハッシュオブジェクトがセットに追加されないようにしています。 python data-model document によると、set()は同じハッシュオブジェクトを同じオブジェクトと見なし、一度追加するだけです。

ただし、以下のように動作が異なります。

_class MyClass(object):

    def __hash__(self):
        return 0

result = set()
result.add(MyClass())
result.add(MyClass())

print(len(result)) # len = 2
_

文字列値の場合、それは正しく動作します。

_result.add('aida')
result.add('aida')

print(len(result)) # len = 1
_

私の質問は、同じハッシュオブジェクトがセット内で同じでないのはなぜですか?

14
Aida.Mirabadi

あなたの読みは正しくありません。 __eq__メソッドは等価チェックに使用されます。ドキュメントには、__hash__の値がa == b(つまりa.__eq__(b))がtrueである2つのオブジェクトaおよびbでも同じである必要があると記載されています。

これはよくあるロジックの間違いです。a == bがtrueである暗黙hash(a) == hash(b)もtrueであること。ただし、含意は必ずしもequivalenceを意味するわけではなく、以前のhash(a) == hash(b)に加えて、 a == b

MyClassのすべてのインスタンスを互いに同等にするには、それらに__eq__メソッドを提供する必要があります。そうでない場合Pythonは代わりにアイデンティティを比較しますこれは次のようになります:

class MyClass(object):
    def __hash__(self):
        return 0
    def __eq__(self, other):
        # another object is equal to self, iff 
        # it is an instance of MyClass
        return isinstance(other, MyClass)

今:

>>> result = set()
>>> result.add(MyClass())
>>> result.add(MyClass())
1

実際には、__hash__の比較に使用されるオブジェクトのプロパティに基づいて__eq__を作成します。次に例を示します。

class Person
    def __init__(self, name, ssn):
        self.name = name
        self.ssn = ssn

    def __eq__(self, other):
        return isinstance(other, Person) and self.ssn == other.ssn

    def __hash__(self):
        # use the hashcode of self.ssn since that is used
        # for equality checks as well
        return hash(self.ssn)

p = Person('Foo Bar', 123456789)
q = Person('Fake Name', 123456789)
print(len({p, q})  # 1
19
Antti Haapala

セットはオブジェクトをハッシュ可能にするためにtwoメソッドを必要とします:__hash__および__eq__。 2つのインスタンスmustは、等しいと見なされたときに同じハッシュ値を返します。両方のハッシュがセットに存在する場合、インスタンスはすでにセットに存在すると見なされますandインスタンスは、セット内に同じハッシュを持つインスタンスの1つと等しいと見なされます。

クラスは__eq__を実装していないため、代わりにデフォルトのobject.__eq__が使用され、obj1 is obj2もtrueの場合にのみtrueを返します。言い換えると、2つのインスタンスが等しいと見なされるのは、それらがまったく同じインスタンスの場合のみです。

それらのハッシュが一致するからといって、セットに関する限り、それらが一意になるわけではありません。テーブルサイズに対するハッシュのmodulusが使用されるため、異なるハッシュを持つオブジェクトでも同じハッシュテーブルスロットに入れられる可能性があります。

2つのインスタンスが等しいと想定される場合にTrueを返すカスタム__eq__メソッドを追加します。

def __eq__(self, other):
    if not isinstance(other, type(self)):
        return False
    # all instances of this class are considered equal to one another
    return True
7
Martijn Pieters