【Python】issubclassでTrueを期待するのにFalseが返ってくる

概要

issubclassを使っていて、Trueを期待するのにFalseが返って来て頭がおかしくなりそうになった。 時間をかけた割に大したことを得られなかった悲しさをこのブログにぶつける。

本題

ある復刻プロジェクトをやっているのだが、そのシステムではdynamoDBを利用している。 dynamoDBを利用するためにはbotoというライブラリを利用しているが、せっかくやるのでbotoboto3にアップデートすることに決めた。

botoを利用する際は、さらにbotoの扱いを抽象化するdynamodb_mapperというものを利用していたが、dynamodb_mapperは最終リリースが2013年であり、更にboto3に対応していないため自分でboto3用のmapperを書かなければいけなかった。

とりあえず動かせそうなmapperを作成して動作確認をしている時に問題が起きた。 ここでmapperのクラス名はDynamoDBModelで、以下のようにしてimportしている。

from dynamodb_mapper.model import DynamoDBModel

そして、今回model_boto3というファイルでmapperを作成したので、以下のようにimport文を書き換える。

from dynamodb_mapper.model_boto3 import DynamoDBModel

新mapperを作ったが、class名はもちろんのことメソッドのインターフェースを変えていないところが自身のこだわったというか、意識した部分だ(コードをきれいにしたらmapperは公開しようと思う)。 しかし、このことが災いを引き起こすなんて思ってもみなかった。

プロジェクトで実際にdynamoDBを利用するためのModelでは、DynamoDBModelクラスを継承している。 以下のようなかんじで。

class Hoge(DynamoDBModel)

このように継承しているのは、システム中で利用しているDBはdynamoDBだけでなくMySQLも利用しており、ModelがDynamoDBModelを継承しているか、MySQL用のクラスを継承しているかによって処理を分けるためだ。

さて、この処理を分ける部分だが、以下のようにしてDynamoDBModelを継承しているかを判断していた。DynamoDBModelのimportではdynamodb_mapper.model_boto3を指定しているし、issubclass()で指定しているDynamoDBModelの部分にマウスポインタを当てると、vscodeがこの型はdynamodb_mapper.model_boto3DynamoDBModelですよって教えてくれているし、これで完璧でしょ、と思った。しかしながら現実はfalse。

issubclass([Hogeのインスタンス], (DynamoDBModel,))

issubclassの使い方が間違っているのかな、と思ったのだが全然間違ってなさそうだし、DynamoDBModeldynamodb_mapper.model_boto3なはずだし、何が悪いのかがさっぱりわからなかった。

と、悩んだり調べたりして2時間経った時にDynamoDBModelはカーソルを乗っけるとdynamodb_mapper.model_boto3って出てくるけど実は旧DynamoDBModelのdynamodb_mapper.modelを参照しているのではないかと思った。 案の定、以下のようにするとtrueが返って来た。

issubclass([Hogeのインスタンス], (dynamodb_mapper.model_boto3.DynamoDBModel,))

実際、こんなフルパスで型指定をしないので旧DynamoDBModelは駆逐して新DynamoDBModelしかない状態にしておいたり、そもそもDynamoDBBoto3Modelみたいな名前にして今後開発を進めて行こうと思った。

関係ないがこの件について調べてみると、pythonのissubclassでハマるという記事があり、抽象クラスに対してはissubclass()は継承していてもfalseを返してくる、という豆知識も学んだ。 時間をかけた割には得たものが小さすぎるが、次へ進もう。

参考

http://takemasa5.tumblr.com/post/8455564961/pythonのissubclassでハマる
takemasa5.tumblr.com