【Python】issubclassでTrueを期待するのにFalseが返ってくる
概要
issubclass
を使っていて、Trueを期待するのにFalseが返って来て頭がおかしくなりそうになった。
時間をかけた割に大したことを得られなかった悲しさをこのブログにぶつける。
本題
ある復刻プロジェクトをやっているのだが、そのシステムではdynamoDBを利用している。
dynamoDBを利用するためにはbotoというライブラリを利用しているが、せっかくやるのでboto
もboto3
にアップデートすることに決めた。
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_boto3
のDynamoDBModel
ですよって教えてくれているし、これで完璧でしょ、と思った。しかしながら現実はfalse。
issubclass([Hogeのインスタンス], (DynamoDBModel,))
issubclass
の使い方が間違っているのかな、と思ったのだが全然間違ってなさそうだし、DynamoDBModel
はdynamodb_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を返してくる、という豆知識も学んだ。 時間をかけた割には得たものが小さすぎるが、次へ進もう。