Pythonは、変数を宣言する際に型を指定する必要がない「動的型付け言語」です。そのため、プログラムの実行中に「この変数には現在どのようなデータが入っているのか」を確認したり、「数値の場合のみ計算を行う」といった型による条件分岐を行いたい場面が発生します。
Pythonには、型を扱うための主な組み込み関数として type() と isinstance() の2つが用意されています。これらは似たような用途に使われますが、継承関係の扱いにおいて決定的な違いがあります。
この記事では、それぞれの基本的な使い方と、その重要な違いについて解説します。
1. type() 関数:型そのものを取得する
type() 関数は、引数として渡されたオブジェクトの「型(クラス)」そのものを返します。主に、デバッグ時に変数の型をコンソールに表示して確認する際などに使用されます。
構文:
type(オブジェクト)
使用例: 数値、文字列、リスト、そして自作クラスのインスタンスの型を確認します。
# 数値 (int)
user_age = 25
print(f"user_age の型: {type(user_age)}")
# 文字列 (str)
user_name = "Tanaka"
print(f"user_name の型: {type(user_name)}")
# リスト (list)
item_list = [100, 200, 300]
print(f"item_list の型: {type(item_list)}")
# 自作クラス
class Product:
pass
# インスタンス化
laptop = Product()
print(f"laptop の型: {type(laptop)}")
実行結果:
user_age の型: <class 'int'>
user_name の型: <class 'str'>
item_list の型: <class 'list'>
laptop の型: <class '__main__.Product'>
このように、<class '型名'> という形式で、そのオブジェクトが属するクラス情報が返されます。
2. isinstance() 関数:特定の型かどうかを判定する
isinstance() 関数は、オブジェクトが「特定の型(クラス)」であるかどうかを判定し、True または False を返します。プログラムの中で「もし文字列なら処理A、数値なら処理B」といった条件分岐を行う場合は、こちらを使用するのが一般的です。
構文:
isinstance(オブジェクト, 型)
使用例: 変数が整数型かどうか、あるいはリスト型かどうかを判定します。
# 数値の判定
score = 85
is_integer = isinstance(score, int)
print(f"score は int ですか?: {is_integer}")
# 文字列の判定
message = "Hello"
is_string = isinstance(message, str)
print(f"message は str ですか?: {is_string}")
# 異なる型での判定
# 文字列に対して「intですか?」と聞く
is_score_int = isinstance(message, int)
print(f"message は int ですか?: {is_score_int}")
実行結果:
score は int ですか?: True
message は str ですか?: True
message は int ですか?: False
また、isinstance(x, (int, float)) のように、第2引数にタプルを渡すことで「整数または浮動小数点数であれば True」といった複数の型に対する判定も可能です。
3. 最も重要な違い:継承関係の扱い
type() と isinstance() の最大の違いは、クラスの継承関係を考慮するかどうかです。
type(): オブジェクトの厳密な型のみを見ます。親クラス(スーパークラス)は「別の型」として扱われます。isinstance(): オブジェクトがそのクラスのインスタンスであるか、またはそのクラスを継承した子クラス(サブクラス)のインスタンスであるかも含めて判定します。
具体的なコード例
「乗り物(Vehicle)」クラスと、それを継承した「車(Car)」クラスを作成して挙動を比較します。
# 親クラス
class Vehicle:
pass
# 子クラス(Vehicleを継承)
class Car(Vehicle):
pass
# それぞれのインスタンスを作成
my_vehicle = Vehicle()
my_car = Car()
print("--- isinstance() の場合(継承を考慮) ---")
# Carのインスタンスは、Car型であり、かつVehicle型でもあるとみなされる
print(f"my_car は Car ですか?: {isinstance(my_car, Car)}")
print(f"my_car は Vehicle ですか?: {isinstance(my_car, Vehicle)}")
print("\n--- type() の場合(厳密に一致) ---")
# Carのインスタンスは、Car型とは一致するが、Vehicle型とは一致しない
print(f"my_car は Car ですか?: {type(my_car) is Car}")
print(f"my_car は Vehicle ですか?: {type(my_car) is Vehicle}")
実行結果:
--- isinstance() の場合(継承を考慮) ---
my_car は Car ですか?: True
my_car は Vehicle ですか?: True
--- type() の場合(厳密に一致) ---
my_car は Car ですか?: True
my_car は Vehicle ですか?: False
isinstance() は「車は乗り物の一種である」という継承関係を正しく認識し True を返しますが、type() は「車クラスと乗り物クラスは別物である」として False を返します。
まとめ:使い分けの指針
Pythonのプログラミングにおいては、基本的には isinstance() を使用することが推奨されます。
これは、将来的に機能を拡張してサブクラスを作った際、isinstance() であれば既存のコード(親クラスを想定した処理)がそのままサブクラスにも適用できるためです(ポリモーフィズム)。
type(): デバッグ目的で型名を表示したい場合や、継承関係を無視して「厳密にそのクラスのインスタンスだけ」を対象にしたい特殊な場合に使用します。isinstance(): 条件分岐で型チェックを行う場合の標準的な選択肢です。
