検索
連載

[Pythonクイズ]型比較にはtype関数を使うべき? isinstance関数を使うべき?Pythonステップアップクイズ

Pythonで型を比較するには大きく2つの方法があります。type関数を使う方法とisinstance関数を使う方法はどこでどんな違いがあるのか、このクイズで確認しましょう。そして、第3の方法もあるって知ってましたか?

PC用表示 関連情報
Share
Tweet
LINE
Hatena
「Pythonステップアップクイズ」のインデックス

連載目次

A、B、C、Dの値はどうなるかな?
A、B、C、Dの値はどうなるかな?

【問題】

 以下はAnimalクラスから派生するDogクラスとCatクラスのオブジェクトを生成し、その型をtype関数とisinstance関数で比較するコードだ。

class Animal:
    pass

class Dog(Animal):
    pass

class Cat(Animal):
    pass

dog = Dog()
cat = Cat()

print(type(dog) is Dog)  # True
print(type(cat) is Cat)  # True
print(type(dog) is Animal)  # A
print(type(cat) is Animal)  # B

print(isinstance(dog, Dog))  # True
print(isinstance(cat, Cat))  # True
print(isinstance(dog, Animal))  # C
print(isinstance(cat, Animal))  # D

A、B、C、Dの値はどうなるかな?

 このコードを実行すると、AからDの値がどうなるかを以下の選択肢から答えよ。

選択肢 A B C D
1 True True True True
2 True True False False
3 False False True True
4 False False False False
A、B、C、Dの値はどうなるかな?

type関数の戻り値を他の型と比較するときにはis演算子と==演算子のどちらを使うべき?

 問題文では「type(dog) is Dog」のようにtype関数の戻り値とDogクラスをis演算子で比較しています。でも、「type(dog) == Dog」とも書けますよね。実際に、筆者もよく書くことがあります。どっちの方がよいのか、ChatGPT(GPT-5)とGemini(2.5 Flash)、Claude(Opus 4.1)の3つの大規模言語モデル(LLM)に聞いてみました。その結果は最後に!



かわさき

 どうもHPかわさきです。

 今回の問題はPythonの世界はよく言及されているので、そんなに難しくはないかもしれません。それだけじゃあナニなので、最後にはおまけの話題もちょびっと付け足しています。そこまで読んでくれるとうれしいです(棒読み)。今回、ここが短いのは書くネタを思いつけなかったからでーす。じゃ、先いってくださいネ。


【答え】

 答えは選択肢3のA:False、B:False、C:True、D:Trueです。

isinstance関数は継承関係を考慮して型を比較してくれるよ
isinstance関数は継承関係を考慮して型を比較してくれるよ

 type関数は引数として与えられたオブジェクトの型を調べるもので、それをそのオブジェクトの親クラスとis演算子や==演算子で比較するとFalseになります。そのため、AとBの値はFalseです。

 一方、isinstance関数は第1引数として与えたオブジェクトが、第2引数として与えたクラスかどうかを継承関係を考慮して判定します。そのため、CとDはTrueになります。

 以下では、これらについてもう少し詳しく説明します。

【解説】

 既に述べましたが、type関数はオブジェクトの型を調べる(取得する)ためのものです。

dog_type = type(dog)
cat_type = type(cat)

print(dog_type)  # <class '__main__.Dog'>
print(cat_type)  # <class '__main__.Cat'>

type関数は与えたオブジェクトの実際の型を調べる

 この例ではdog_typeは__main__モジュール(Pythonの対話環境など)で定義されているDogクラスです。同様に、cat_typeはCatクラスです。これらをis演算子でその親クラスであるAnimalクラスと比較しているのが、問題文にあるAとBの部分です。

class Animal:
    pass

class Dog(Animal):
    pass

class Cat(Animal):
    pass

dog = Dog()
cat = Cat()

print(type(dog) is Dog)  # True
print(type(cat) is Cat)  # True
print(type(dog) is Animal)  # A
print(type(cat) is Animal)  # B

DogクラスとAnimalクラス、CatクラスとAnimalクラスを比較

 4つあるprint関数呼び出しの最初の2つはDogクラスとDogクラス、CatクラスとCatクラスをis演算子で比較しているので、その結果はもちろんTrueです。後の2つはDogクラスとAnimalクラス、CatクラスとAnimalクラスをis演算子で比較しているので、その結果はもちろんFalseになるというわけです。

 対して、isinstance関数は継承関係を考慮して、第1引数として与えたオブジェクトが第2引数として与えたクラスであるかどうか、つまり「is-a」の関係が成立しているかどうかを調べるものです。これが問題文のCとDの部分です。

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))  # C
print(isinstance(cat, Animal))  # D

「dogオブジェクトはAnimalクラスのオブジェクトかどうか」「catオブジェクトはAnimalクラスのオブジェクトかどうか」をisinstance関数で調べる

 isinstance関数のドキュメントには「object引数がclassinfo引数に指定した型、またはその(直接、間接、または仮想の)サブクラスのインスタンスである場合にTrueを返します。objectが与えられた型のオブジェクトでない場合、この関数は常にFalseを返します」とあります(ここでobject引数とは第1引数のことで、classinfo引数とは第2引数のこと)。

 そして、DogクラスとCatクラスはどちらもAnimalクラスを継承しています(DogクラスとCatクラスはAnimalクラスのサブクラス)。つまり、「Dog is an Animal」「Cat is an Animal」の関係が成り立っています。そのため、DogクラスとCatクラスのオブジェクトはどちらもAnimalクラスのオブジェクトでもあるということです。よって、CとDはTrueになります。

 オブジェクトの型を知りたいだけであれば、type関数を使えばよいでしょう。実際、筆者も「print(type(some_obj))」のような使い方はよくしますし、得られたクラスをhelp関数に渡してドキュメントを参照したり、dir関数に渡してどんな属性やメソッドがあるかを確認したりしています。

 そうではなく、オブジェクトの型を何らかの型と比較したい場合、特に継承関係を考慮して比較したい場合には、type関数で得た型をis演算子や==演算子で比較するのではなく、isinstance関数を使って判定することをオススメします。

 あるいはmatch文のクラスパターンを使う方法も考えられます(Python 3.10以降)。

class Animal:
    pass

class Dog(Animal):
    pass

class Cat(Animal):
    pass

class Bird(Animal):
    pass

dog = Dog()
cat = Cat()
bird = Bird()

objs = [dog, cat, bird]
for obj in objs:
    match obj:
        case Dog():
            print('dog')
        case Cat():
            print('cat')
        case Animal():
            print('animal')
        case _:
            print('none')

match文のクラスパターンを使うと継承関係を考慮してオブジェクトのパターンマッチが行われる

 この例ではAnimalを継承するBirdクラスも追加して、dogとcatとbirdの3つのオブジェクトを生成して、その型に応じて処理を切り替えるようにmatch文でパターンマッチしています。ここでは各クラスに属性がないので「case Dog()」のような書き方になっていますが、オブジェクトに属性があり、その属性の値ごとに処理を切り分けることも可能です(説明は省略)。

 重要なのはクラスパターンではisinstance関数と同様に継承関係を考慮したパターンマッチが行われます。特に「case Animal():」は「isinstance(obj, Animal)」のような判定が行われると考えてください。そのため、上のサンプルコードではdogオブジェクトは「case Dog():」に、catオブジェクトは「case Cat():」に、birdオブジェクトは「case Animal():」にマッチします。

 match文とクラスパターンを頭の中に入れておくと、if文とisinstance関数の組み合わせよりもシンプルにコードを書けるかもしれません。ただし、サブクラスのcase節をベースクラスのcase節よりも先に置く必要がある点には注意してください。この例では「case Animal():」が他のcase節よりも先にあると、dogオブジェクトもcatオブジェクトもそこでマッチしてしまいます。


かわさき

 では、type関数の戻り値を他の型と比較するときにはis演算子と==演算子のどちらを使うべきなのかを3つのLLMに聞いた結果です。

 全員一致でis演算子でした! その理由も共通しています。その前に2つの演算子の違いを簡単にまとめると次のようになります。

  • is演算子:同一性の比較
  • ==演算子:同値性の比較

 そして、ある型が別の型と同じかどうかを調べるには、同値性ではなく同一性を比較すべきだからということになります。承知しました。次からはちゃんとis演算子を使うようにします(笑)。


「Pythonステップアップクイズ」のインデックス

Pythonステップアップクイズ

Copyright© Digital Advantage Corp. All Rights Reserved.

[an error occurred while processing this directive]
ページトップに戻る