「このオブジェクト、どのクラスのものなんだろう?」——初めてオブジェクト指向に触れると、こうした疑問が自然と湧いてきます。
Pythonには、まさにこの疑問に答えるための関数が用意されています。それがisinstance()
。特定のオブジェクトが、あるクラス(あるいはそのサブクラス)のインスタンスかどうかを、安全かつ読みやすく判断できます。
今回はisinstance()について詳しく解説します。「type() と どう違うの?」「複数のクラスをまとめてチェックしたい!」といった疑問にも順を追って答えます。
isinstance() の使い方はとてもシンプルです。次の形で呼び出します。
isinstance(object, classinfo)
第1引数には調べたいオブジェクトを、第2引数には調べたいクラス(もしくはクラスのタプル)を渡します。 もし object が classinfo のインスタンスであれば True、そうでなければ False が返ってきます。
たとえば次の例を見てみましょう。
class Animal:
pass
class Dog(Animal):
pass
class Cat(Animal):
pass
dog = Dog()
cat = Cat()
print(isinstance(dog, Dog)) # True
print(isinstance(cat, Cat)) # True
print(isinstance(dog, Animal)) # True
print(isinstance(cat, Animal)) # True
print(isinstance(dog, Cat)) # False
ここでは、Animal を親(基底)クラスにして、Dog と Cat を子(サブ)クラスとして定義しています。dog は Dog のインスタンスなので True。さらに、Dog は Animal を継承しているため、dog は Animal としても扱えることになり、isinstance(dog, Animal) も True になります。一方で、dog は Cat ではないので False。継承関係をそのまま反映して判断してくれるのが、isinstance() の大きな強みです。
「Dog か Cat のどちらかなら OK」というように、複数候補をまとめて確認したいこともありますよね。 そんなときは、クラスをタプルで渡します。
class Animal:
pass
class Dog(Animal):
pass
class Cat(Animal):
pass
dog = Dog()
cat = Cat()
print(isinstance(dog, (Dog, Cat))) # True
print(isinstance(cat, (Dog, Cat))) # True
第2引数に (Dog, Cat) のようなタプルを渡すと、どれか1つにでも当てはまれば True になります。 たとえば、文字列またはバイト列を受け付けるといった入力チェックにも応用できます。
「型を調べるなら type() でもいいのでは?」と感じた方もいるかもしれません。
結論から言うと、継承を活かした設計では isinstance() を使うのが基本です。 type() は「まさにそのクラスかどうか」しか見ませんが、isinstance() は「サブクラスも含めて」判断します。
違いがわかる短い例を見てみましょう。
class Animal:
pass
class Dog(Animal):
pass
dog = Dog()
print(type(dog) is Dog) # True
print(type(dog) is Animal) # False(Dog は Animal のサブクラスなので一致しない)
print(isinstance(dog, Animal)) # True(サブクラスも含めて判断)
多態性(ポリモーフィズム)を活かしたコードでは、「親クラスとして扱えるか」を確認する場面が多くなります。そのときに type() を使ってしまうと、継承の恩恵を受けにくくなり、柔軟性が下がってしまいます。迷ったら isinstance() を選ぶ、これが実務でもよく使われる基本の考え方です。
ここまで学習した内容をまとめます。
「このオブジェクト、どう扱えばいい?」と迷ったら、まずは isinstance() で優しく確かめる。そんな第一歩から、継承や多態性を活かした読みやすい Python コードへとつなげていきましょう。