Pythonで型判定を行う際、isinstance()とtype()は似ているようで目的が異なります。
初心者の方が混乱しやすい点は、継承を考慮するかどうかです。
本記事では、両者の違いを明確にし、実用的な使い分けとベストプラクティスをサンプルコードとともに丁寧に解説します。
Pythonのisinstance()とtype()の違い
継承を考慮する型判定はisinstance
基本の考え方とシグネチャ
isinstance(obj, cls_or_tuple)は、サブクラスも含めて判定します。
第2引数にはクラス(型)か、クラス(型)のタプルを渡せます。
つまり、ある抽象的な「系統」に属するかどうかを調べるのに向いています。
# 継承関係を持つクラスを定義
class Animal:
pass
class Dog(Animal):
pass
dog = Dog()
# isinstance はサブクラスを「親としても」認める
print(isinstance(dog, Dog)) # Dogのインスタンスか
print(isinstance(dog, Animal)) # Animal(親)としても扱えるか
True
True
タプル指定で複数型を許容
複数の受け入れ可能な型を並べるときは、タプルで渡します。
これにより冗長な条件分岐を避け、読みやすく安全なコードになります。
詳細は後述の「複数の許容型はisinstanceのタプル指定が便利」を参照してください。
厳密な型一致の確認はtypeで比較
type()は「ぴったり一致」を見る
type(obj) is SomeClassのように比較すると、サブクラスを認めません。
厳密な一致が必要なときに使います。
例えば、サブクラスを誤って受け入れたくない場合に有効です。
class Animal:
pass
class Dog(Animal):
pass
dog = Dog()
print(type(dog) is Dog) # ぴったりDogか
print(type(dog) is Animal) # 親クラスAnimalそのものか
True
False
比較演算子はisが基本
type(obj) == SomeClassより、type(obj) is SomeClassを用いるのが一般的です。
クラスオブジェクトは単一性が保証されるため、is
での同一性比較が適切です。
組み込み型とユーザー定義クラスでの挙動の差
代表的な違いを早見表で整理
観点 | isinstance(obj, 型) | type(obj) is 型 |
---|---|---|
継承を考慮 | する | しない |
複数型を一度に判定 | 可能(タプル指定) | 不可 |
代表的な用途 | インターフェースや系統(親子関係)の確認 | 厳密な一致、サブクラス除外 |
注意点 | 過剰に使うとダックタイピングを阻害 | 継承を無視してしまう |
特にboolはintのサブクラス(Python仕様)である点は初心者がつまずきやすいです。
isinstance(True, int)はTrueになりますが、type(True) is intはFalseです。
詳細は「Noneやboolなど特殊ケースの型判定のコツ」で扱います。
Pythonでの正しい使い分け
サブクラスやインターフェース判定はisinstanceが安全
継承を活かすAPIではisinstance
ライブラリやアプリケーションのAPIで拡張(サブクラス)を受け入れたいとき、isinstanceを使うと柔軟性を保てます。
例えば、Animal
を受け入れる関数は、Dog
やCat
などのサブクラスも自然に扱えます。
class Animal:
def speak(self):
return "..."
class Dog(Animal):
def speak(self):
return "woof"
def call_speak(x):
# Animal系を受け入れる設計
if not isinstance(x, Animal):
raise TypeError("Animalの系統ではありません")
return x.speak()
print(call_speak(Dog()))
woof
複数の許容型はisinstanceのタプル指定が便利
bytes/bytearray/strを受け入れる例
外部入力では文字列かバイト列かを柔軟に扱いたい場面が多いです。
そんなときはタプル指定で分かりやすく書けます。
# 受け取った値を「文字列」に正規化する関数
def normalize_to_str(x):
# bytes と bytearray のどちらも許容
if isinstance(x, (bytes, bytearray)):
# bytearrayもbytesに変換すればdecode可能
x = bytes(x)
return x.decode("utf-8")
elif isinstance(x, str):
return x
else:
raise TypeError("str, bytes, bytearrayのみ受け付けます")
print(normalize_to_str(b"hello")) # bytes
print(normalize_to_str(bytearray(b"hi"))) # bytearray
print(normalize_to_str("こんにちは")) # str
hello
hi
こんにちは
Noneやboolなど特殊ケースの型判定のコツ
Noneは型判定しない。
「is None」を使う
Noneチェックはis None
が定石です。
type(x) is NoneType
のような書き方は避けます。
x = None
# Noneかどうかは同一性で判定するのが正解
if x is None:
print("xはNoneです")
else:
print("xはNoneではありません")
xはNoneです
boolはintのサブクラスである
isinstance(True, int)はTrueです。
真偽値を整数として数えたくない場面では注意します。
print(isinstance(True, int)) # boolはintのサブクラスなのでTrue
print(isinstance(True, bool)) # もちろんTrue
print(type(True) is int) # 厳密にintそのものか → False
print(type(True) is bool) # 厳密にboolそのものか → True
def want_int_but_not_bool(x):
# boolを明確に除外してからint判定
if isinstance(x, bool):
raise TypeError("boolは除外します")
if not isinstance(x, int):
raise TypeError("intが必要です")
return x + 1
print(want_int_but_not_bool(10))
True
True
False
True
11
型判定のベストプラクティス
ダックタイピングを優先しisinstanceは最小限に
EAFP(まず試して、ダメなら例外)のスタイル
Pythonでは「できるかどうか」を型ではなく振る舞いで判断するのが自然です。
ダックタイピング(アヒルのように歩き鳴くなら、それはアヒル)を優先し、必要最小限の場面だけisinstanceを使います。
# 先頭要素を取りたい。「シーケンスか?」ではなく「添字アクセスできるか?」で判断
def head(seq):
try:
return seq[0] # 添字アクセスできればOK
except (TypeError, IndexError):
return None
print(head([1, 2, 3])) # リスト
print(head("abc")) # 文字列
print(head(123)) # 添字不可 → None
1
a
None
抽象基底クラスcollections.abcでのisinstance活用
ABCを使うと「インターフェース」による判定ができる
collections.abcのSequence
やMapping
などの抽象基底クラス(ABC)でインターフェース判定が可能です。
自作クラスが対応メソッドを実装すれば、isinstance
で「その振る舞いを持つ」と認められます。
from collections.abc import Sequence, Mapping, Iterable
def first_three(x):
if isinstance(x, Sequence): # シーケンスプロトコル(順序・添字・len)
return x[:3]
raise TypeError("Sequenceではありません")
print(isinstance([1, 2, 3], Sequence)) # listはSequence
print(isinstance({"a": 1}, Mapping)) # dictはMapping
print(isinstance((i for i in range(3)), Iterable)) # ジェネレータはIterable
print(first_three(("A", "B", "C", "D"))) # タプルにも適用
True
True
True
('A', 'B', 'C')
アンチパターンとパフォーマンスの注意点
避けたいアンチパターン
- type比較でサブクラスを排除してしまう: 拡張性が必要なAPIでは
isinstance
を使いましょう。 - 型分岐が過剰: まずはポリモーフィズム(各クラスに共通メソッドを持たせる)やダックタイピングで設計を見直します。
- Noneをtypeで判定:
is None
を使います。
パフォーマンスの話
isinstanceやtype自体は高速ですが、無意味な回数の判定は避けるべきです。
熱いループ内では、if
の外で一度だけ判定する、責務を分離して分岐自体をなくすなど、設計で最適化するのが効果的です。
まとめとチェックリスト
isinstanceを使う場面とtypeを使う場面の整理
- isinstance: 継承やインターフェース(ABC)を考慮して受け入れたいとき。複数型許容はタプルで簡潔に。
- type(… ) is …: サブクラスを厳密に排除したいときや、完全一致を保証したいとき。
初心者が避けたい型判定の落とし穴
- Noneチェックにtypeを使わない(
is None
を使う)。 - boolはintのサブクラスであることを理解し、整数扱いしたくない場合は除外する。
- ダックタイピングを忘れない。まずは「できるか」で判定し、やむを得ないときだけ型を見る。
- 拡張性が必要なAPIでtype比較を使わない。サブクラスを受け入れられるよう
isinstance
を選ぶ。
まとめ
isinstance()は継承や抽象基底クラスを考慮した柔軟な判定、type()はサブクラスを排除する厳密な一致確認に適しています。
Noneはis None
で判定し、boolがintのサブクラスである点に注意します。
実務ではダックタイピングを優先し、必要最小限の型判定に留めることが読みやすさと拡張性につながります。