【Python】isinstance()とissubclass()の違いとは?型チェックの基本をコードで徹底解説!

Python

はじめに:Pythonの型チェックで迷っていませんか?

Pythonでプログラミングをしていると、「このオブジェクトは、本当にあのクラスのインスタンスなのかな?」とか「このクラスは、ちゃんとあのクラスを継承しているかな?」と確認したくなる場面がよくあります。

そんな時に役立つのが、Pythonに標準で用意されている isinstance()issubclass() という2つの組み込み関数です。

しかし、名前が似ているため、 「どっちが何をする関数だっけ?」 「どう使い分ければいいの?」 と混乱してしまう方も少なくありません。

この記事を読めば、isinstance()issubclass()の明確な違いと、それぞれの正しい使い方が分かります。具体的なコード例を交えながら丁寧に解説するので、ぜひ最後まで読んでマスターしてください!

isinstance()とは?オブジェクトの型を調べる関数

isinstance()は、指定したオブジェクト(モノ)が、特定のクラス(種類)またはそのサブクラスのインスタンスであるかどうかを判定する関数です。結果はTrueFalseの真偽値で返されます。

isinstance()の基本的な使い方

isinstance()は、2つの引数を取ります。

isinstance(object, classinfo)
  • object: 型を調べたいオブジェクト
  • classinfo: 比較対象のクラス、またはクラスのタプル

言葉だけだと分かりにくいので、早速コードを見てみましょう。

# 文字列の例
my_string = "hello"
print(f"'hello'はstr型ですか? -> {isinstance(my_string, str)}") # -> True

# 整数の例
my_int = 100
print(f"100はint型ですか? -> {isinstance(my_int, int)}") # -> True
print(f"100はstr型ですか? -> {isinstance(my_int, str)}") # -> False

# リストの例
my_list = [1, 2, 3]
print(f"[1, 2, 3]はlist型ですか? -> {isinstance(my_list, list)}") # -> True

このように、オブジェクトが指定したクラスから作られたインスタンスであればTrueを返します。非常に直感的で分かりやすいですね。

継承関係も考慮されるのがポイント

isinstance()の強力な点は、クラスの継承関係も考慮してくれることです。つまり、親クラスを指定した場合でも、その子クラスのインスタンスであればTrueと判定されます。

簡単な動物クラスで確認してみましょう。

Python

# 親クラス
class Animal:
    pass

# 子クラス
class Dog(Animal):
    pass

# インスタンスを作成
inu = Dog()

# DogはDogクラスのインスタンスか? -> True
print(f"inuはDogクラスのインスタンスですか? -> {isinstance(inu, Dog)}")

# Dogは親クラスであるAnimalクラスのインスタンスでもあると判定される
print(f"inuはAnimalクラスのインスタンスですか? -> {isinstance(inu, Animal)}")

出力結果:

inuはDogクラスのインスタンスですか? -> True
inuはAnimalクラスのインスタンスですか? -> True

DogクラスはAnimalクラスを継承しているので、DogのインスタンスであるinuAnimalの一種と見なされます。これがisinstance()の重要な特徴です。

複数の型をまとめてチェックする方法

第2引数にクラスをタプルで渡すことで、複数の型を一度にチェックできます。いずれかの型に一致すればTrueが返ります。

value = 123.45

# valueはint型またはfloat型ですか?
is_number = isinstance(value, (int, float))

print(f"123.45はint型またはfloat型ですか? -> {is_number}") # -> True

value2 = "text"
is_number2 = isinstance(value2, (int, float))
print(f"'text'はint型またはfloat型ですか? -> {is_number2}") # -> False

これは、isinstance(value, int) or isinstance(value, float) と書くのと同じ意味になります。タプルを使う方が簡潔で読みやすいコードになります。

issubclass()とは?クラスの継承関係を調べる関数

issubclass()は、ある**クラス(設計図)が、別のクラス(設計図)を継承しているかどうか(サブクラスであるか)**を判定する関数です。こちらも結果はTrueFalseで返されます。

issubclass()の基本的な使い方

issubclass()も2つの引数を取ります。

issubclass(class, classinfo)
  • class: 判定したいクラス
  • classinfo: 比較対象の親クラス、または親クラスのタプル

isinstance()と違い、引数にオブジェクトではなくクラスそのものを渡す点に注意してください。

# 先ほどと同じAnimalとDogクラスを使用
class Animal:
    pass

class Dog(Animal):
    pass

class Cat:
    pass

# DogクラスはAnimalクラスのサブクラスか? -> True
print(f"DogはAnimalのサブクラスですか? -> {issubclass(Dog, Animal)}")

# AnimalクラスはDogクラスのサブクラスか? -> False
print(f"AnimalはDogのサブクラスですか? -> {issubclass(Animal, Dog)}")

# CatクラスはAnimalクラスのサブクラスか? -> False
print(f"CatはAnimalのサブクラスですか? -> {issubclass(Cat, Animal)}")

出力結果:

DogはAnimalのサブクラスですか? -> True
AnimalはDogのサブクラスですか? -> False
CatはAnimalのサブクラスですか? -> False

このように、クラス間の親子関係(継承関係)を直接調べることができます。

自分自身のクラスもTrueになる

issubclass()では、あるクラスは自分自身のサブクラスであると見なされます。つまり、同じクラスを比較するとTrueが返ります。

class Animal:
    pass

print(f"AnimalはAnimalのサブクラスですか? -> {issubclass(Animal, Animal)}") # -> True

これは仕様上の挙動であり、「すべてのクラスはそれ自身のサブクラスである」と定義されています。

決定的な違い:「モノ」を調べるか「設計図」を調べるか

isinstance()issubclass()の最も重要な違いを、一言でまとめます。

  • isinstance(): **オブジェクト(モノ)**が、特定のクラスから作られたものかを調べる。
  • issubclass(): **クラス(設計図)**が、別のクラスを継承しているかを調べる。

ポイントは「インスタンス」と「クラス」

inu = Dog() というコードがあった場合、

  • inuDogクラスから作られた**インスタンス(オブジェクト)**です。
  • DogAnimalクラスです。

この違いを意識すれば、isinstance(inu, Dog)のように「インスタンス」を調べるのがisinstance()で、issubclass(Dog, Animal)のように「クラス」を調べるのがissubclass()だと、迷うことはなくなるでしょう。

使い分けの簡単ガイド

やりたいこと使う関数
変数xの中身が数値(intやfloat)か調べたいisinstance()isinstance(x, (int, float))
MyClassBaseClassを継承しているか確認したいissubclass()issubclass(MyClass, BaseClass)
受け取った引数がUserクラスのオブジェクトか調べたいisinstance()isinstance(user_obj, User)

Google スプレッドシートにエクスポート

参考:type()との違い

isinstance()とよく比較されるのがtype()関数です。type(obj) == MyClass のように型を比較できますが、これは継承関係を考慮しません

class Animal:
    pass

class Dog(Animal):
    pass

inu = Dog()

print(f"type(inu) == Dog -> {type(inu) == Dog}")           # -> True
print(f"type(inu) == Animal -> {type(inu) == Animal}")     # -> False
print(f"isinstance(inu, Animal) -> {isinstance(inu, Animal)}") # -> True

type()は厳密に型が一致するかどうかしか見ませんが、isinstance()は継承関係を遡ってチェックします。柔軟な型チェックを行いたい場合は、type()よりもisinstance()を使うのが一般的です。

実践的な使い方

関数の引数チェックで安全なコードを書く

関数の引数として特定の型のオブジェクトを期待する場合、isinstance()を使って型チェックを行うことで、予期せぬエラーを防ぎ、コードの堅牢性を高めることができます。

def process_data(data):
    # dataがリストでなければ処理を中断
    if not isinstance(data, list):
        raise TypeError("引数dataはリストである必要があります")
    
    # リストに対する処理
    for item in data:
        print(item)

# 正しい使い方
process_data([1, 2, 3])

# 間違った使い方(TypeErrorが発生)
# process_data("hello")

ポリモーフィズムと組み合わせて柔軟な処理を実現

ポリモーフィズム(多態性)を活かしたコーディングでは、オブジェクトの種類に応じて処理を分岐させたい場合があります。isinstance()を使えば、これをスマートに実装できます。

class Dog:
    def speak(self):
        return "ワン!"

class Cat:
    def speak(self):
        return "ニャー"

def animal_sound(animal):
    if isinstance(animal, (Dog, Cat)):
        print(animal.speak())
    else:
        print("犬か猫ではありません")

inu = Dog()
neko = Cat()
animal_sound(inu)  # -> ワン!
animal_sound(neko) # -> ニャー

まとめ

今回は、Pythonのisinstance()issubclass()について、その違いと使い方を解説しました。最後に重要なポイントをまとめます。

  • isinstance(object, class): オブジェクトが特定のクラス(またはそのサブクラス)のインスタンスか調べる。
  • issubclass(class, class): あるクラスが別のクラスのサブクラス(継承関係にある)か調べる。
  • チェック対象が「モノ(インスタンス)」なのか「設計図(クラス)」なのかを意識するのが使い分けの鍵。
  • 継承関係を考慮した柔軟な型チェックには type() ではなく isinstance() を使うのが一般的。

この2つの関数を正しく使い分けることで、より安全で読みやすいPythonコードを書くことができます。ぜひ今後のコーディングに活かしてみてください。

コメント

タイトルとURLをコピーしました