2017/10/03更新

[Python] 独自クラスをSetやDictのキーで使えるように、ハッシュ対応する

このエントリーをはてなブックマークに追加      

こんにちは、@yoheiMuneです。
Pythonはやっぱり便利ですね。仕事ではGo言語を使ってますが、プライベートではPythonを使いまくってます。今日は独自クラスをセット型や、辞書型のキーで使う方法をブログに書きたいと思います。
画像


目次




独自クラスとSetやDict

今回は、以下のUserクラスを例に、話を進めていきます。
class User(object):
    def __init__(self, id, name):
        self.id = id
        self.name = name
これをインスタンス化してSetに登録するとエラーなく登録できますが、「同じ」という定義に注意する必要があります。Setでは「同じもの」を1つだけ保持できますが、ここでの「同じ」という定義はメモリ番地で判断されてます。
# インスタンスを作成
user1 = User(1, "Yohei")
user2 = User(2, "Satoshi")
user3 = User(3, "Kento")
user4 = User(1, "Shunji")  # idが一緒

# Setを作る
s = set([user1, user2, user3, user4])

# 中身を確認する
print(len(s))  # 4
print(s) # {<__main__.User object at 0x1021b4898>, <__main__.User object at 0x1021b49e8>, <__main__.User object at 0x1021b4940>, <__main__.User object at 0x1021b4860>}
同じく、辞書のキーでも同様に「同じ」定義はメモリ番地が利用されます。
d = dict([(user1, 30), (user4, 25)])
len(d) # 2
print(d) # {<__main__.User object at 0x1021b4860>: 30, <__main__.User object at 0x1021b49e8>: 25}
この挙動で問題なければそれでOKなのですが、「同じ」という定義をカスタマイズしたい場合があります。
今日はその実装方法をブログに書きました。



独自クラスでの、Setにおける「同じ」定義をカスタマイズする

「同じ」という定義をカスタマイズするには、__eq__メソッドと__hash__メソッドを実装する必要があります。以下の例ではUserクラスのidが一致するか否かで、同一判定を実装しました。
class User(object):
    
    def __init__(self, id, name):
        self.id = id
        self.name = name

    def __eq__(self, other):
        if not isinstance(other, User):
            return False
        return self.id == other.id

    def __hash__(self):
        return hash(self.id)
公式ドキュメントを読むと分かりますが、「__eq____hash__の両方」を実装しないと、同一判定が正しく行われないので注意が必要です。

上記を実装したUserでは、idが重複するものはSetで1つしか登録できません。
# インスタンスを作成
user1 = User(1, "Yohei")
user2 = User(2, "Satoshi")
user3 = User(3, "Kento")
user4 = User(1, "Shunji")  # idが一緒

# Setを作る
s = set([user1, user2, user3, user4])

# 中身を確認する
print(len(s))  # 3
for user in s:
    print(user.id, user.name)
    # 1 Yohei
    # 2 Satoshi
    # 3 Kento
辞書のキーでも同様に、idで重複する場合には1つしか登録できません。
d = dict([(user1, 30), (user4, 25)])
len(d) # 1
このように、__eq____hash__という特殊メソッドを実装することで、独自クラスのハッシュ化の振る舞いを定義することができます。とっても便利ですね。


参考資料

以下の公式ドキュメントを参考に実装を行いました。
仕様の細かい内容や注意点もありますので、詳細は以下のリンクをご確認ください。

https://docs.python.jp/3/reference/datamodel.html#object.__hash__



最後に

Pythonは学べば学ぶほど便利に使える言語でいいなーと思います。これからもたくさんPythonネタは書きたいと思いますので、これからもよろしくお願いします。

最後になりますが本ブログでは、Go言語・Python・Linux・フロントエンド・Node.js・インフラ・開発環境・Swift・Java・機械学習など雑多に情報発信をしていきます。自分の第2の脳にすべく、情報をブログに貯めています。気になった方は、本ブログのRSSTwitterをフォローして頂けると幸いです ^ ^。

最後までご覧頂きましてありがとうございました!





こんな記事もいかがですか?

RSS画像

もしご興味をお持ち頂けましたら、ぜひRSSへの登録をお願い致します。