Pythonのtypesモジュールの使い方を初心者向けに解説!SimpleNamespaceや型判定の基本
Pythonを学んでいると、int、str、list のような型はよく見かけます。
ですが、少し学習が進むとtypesという標準ライブラリに出会って、これは何に使うのだろうと感じる方も多いのではないでしょうか。
結論から言うと、Pythonの types モジュールは 型に関する便利な道具をまとめた標準ライブラリ です。普段の入門コードではあまり目立ちませんが、オブジェクトの種類を調べたり、簡単なデータ入れ物を作ったり、読み取り専用の辞書ビューを作ったりするときに役立ちます。
この記事では、IT初心者の方にもわかるように、Pythonの types の使い方をやさしく解説します。
Pythonのtypesモジュールとは¶
Pythonのtypesモジュールは、Pythonに最初から入っている標準ライブラリの1つです。追加インストールは不要で、import typesと書くだけで使えます。
公式ドキュメントでは、typesは動的な型作成を助ける関数や、Pythonインタプリタで使われる一部の型名、型に関するユーティリティを提供するモジュールとして説明されています。 つまり、型そのものを扱うための補助ツール集と考えるとわかりやすいです。
まずは、読み込み方を見てみましょう。
import types
print(types)
typesは標準ライブラリなので、pip installなどは必要ありません。Pythonをインストールしていれば、すぐに使えます。
ただし、初心者の方に最初に伝えたいのは、types は毎日必ず使うモジュールではないということです。 まずは int、str、list、dict、関数、クラスといった基本を理解したあとに触れると、かなり理解しやすくなります。
type関数とtypesモジュールの違い¶
名前が似ているので、typeとtypesを混同しやすいです。ここは最初に整理しておきましょう。
typeはPythonの組み込み関数です。値の型を調べたり、クラスを動的に作ったりできます。
一方でtypesは標準ライブラリのモジュールです。FunctionTypeやSimpleNamespaceのように、型に関係する部品がいくつも入っています。
表にすると、違いは次のようになります。
| 名前 | 種類 | 主な使い方 | 初心者向けの理解 |
|---|---|---|---|
| type | 組み込み関数 | 値の型を調べる | これは何型かを確認する関数 |
| types | 標準ライブラリ | 型関連の便利機能を使う | 型に関する道具箱 |
| typing | 標準ライブラリ | 型ヒントを書く | コードの意図を伝えるための型情報 |
type、types、typing は名前が似ていますが、役割は違います。特にtypingは型ヒントでよく使うため、typesと分けて理解しておくと混乱しにくいです。
【関連記事】Pythonの型ヒントをどうやって使うの?リーダブルコードに必須な型チェック
まずはtype関数で型を確認してみる¶
types モジュールに入る前に、まず type関数で型を確認してみましょう。Pythonでは、あらゆる値が何らかの型を持っています。
print(type(123))
print(type("hello"))
print(type([1, 2, 3]))
print(type({"name": "Taro"}))
実行すると、次のような結果になります。
<class 'int'>
<class 'str'>
<class 'list'>
<class 'dict'>
intは整数、strは文字列、listはリスト、dictは辞書です。 Pythonでは、これらもすべてオブジェクトとして扱われます。
ここで大切なのは、Pythonでは型もプログラム上で扱えるという感覚です。
typesモジュールは、この型の世界を少し便利に扱うための道具だと思ってください。
typesでよく使うもの一覧¶
typesモジュールには多くの名前が用意されています。ですが、初心者がすべて覚える必要はありません。
まずは、実用的に見かけやすいものを表で確認しましょう。
| 名前 | 役割 | 使いどころ |
|---|---|---|
| types.SimpleNamespace | 属性でアクセスできる簡単なオブジェクトを作る | テストデータや設定値の入れ物 |
| types.MappingProxyType | 辞書を読み取り専用のように扱う | 設定値の保護、公開用ビュー |
| types.FunctionType | 関数オブジェクトの型 | 関数かどうかの判定 |
| types.LambdaType | lambdaで作られた関数の型 | ラムダ式を含む関数判定 |
| types.ModuleType | モジュールの型 | モジュールかどうかの確認 |
| types.NoneType | None の型 | Noneを型として扱いたい場合 |
この中でも、初心者にとって理解しやすく、実用イメージが湧きやすいのは SimpleNamespaceとMappingProxyTypeです。 この記事でも、この2つを中心にしつつ、型判定に関係するものを紹介します。
SimpleNamespaceの使い方¶
SimpleNamespaceは、簡単なオブジェクトを作るためのクラスです。 辞書のようにデータを持てますが、ドットで属性にアクセスできるのが特徴です。
まずは、基本的な使い方を見てみましょう。
from types import SimpleNamespace
user = SimpleNamespace(name="Taro", age=30)
print(user.name)
print(user.age)
実行結果は次のようになります。
Taro
30
辞書ならuser["name"]のように書きますが、SimpleNamespaceではuser.nameと書けます。コードが少し自然な文章のように読めますね。
辞書との違い¶
SimpleNamespaceは辞書に似ていますが、まったく同じものではありません。 辞書はキーで値にアクセスし、SimpleNamespaceは属性で値にアクセスします。
比較してみると、違いがわかりやすいです。
from types import SimpleNamespace
user_dict = {"name": "Taro", "age": 30}
user_obj = SimpleNamespace(name="Taro", age=30)
print(user_dict["name"])
print(user_obj.name)
どちらも Taro を取り出せます。ですが、書き方が違います。
表で整理すると、次のようになります。
| 項目 | dict | SimpleNamespace |
|---|---|---|
| アクセス方法 | user["name"] | user.name |
| キーの自由度 | 文字列以外も使える | 属性名として使える名前が基本 |
| 用途 | 汎用的なデータ管理 | 簡単なオブジェクト風のデータ |
| 初心者へのおすすめ | まず覚えるべき基本 | 補助的に使うと便利 |
基本はdictを覚えるべきです。 SimpleNamespaceは、ちょっとした設定やテスト用データを見やすく持ちたいときに使うと便利です。
エンジニア歴10年の私の経験では、SimpleNamespaceは本番の複雑なデータ設計よりも、テストコードやサンプルコードで力を発揮する印象があります。 簡単なモックデータを作るときに、読みやすく書けるからです。
後から属性を追加できる¶
SimpleNamespaceは、作ったあとから属性を追加できます。これも初心者にとってわかりやすい特徴です。
from types import SimpleNamespace
user = SimpleNamespace(name="Taro")
user.age = 30
user.country = "Japan"
print(user)
実行結果は次のようになります。
namespace(name='Taro', age=30, country='Japan')
このように、user.ageやuser.countryのように後から追加できます。
ちょっとしたデータの入れ物としては便利です。
ただし、何でも後から足せるということは、構造が曖昧になりやすいということでもあります。
大きなアプリケーションでは、dataclassや通常のクラスを使ったほうが安全なことも多いです。
【関連記事】Pythonの変数は箱じゃない?メモリ上のラベルという正体を理解する
MappingProxyTypeの使い方¶
次に、MappingProxyType を見ていきます。これは、辞書の読み取り専用ビューを作るためのクラスです。
読み取り専用と聞くと少し難しそうですが、要するに外から勝手に変更されたくない辞書を見せるための仕組みです。
from types import MappingProxyType
settings = {
"debug": False,
"version": "1.0.0",
}
readonly_settings = MappingProxyType(settings)
print(readonly_settings["debug"])
print(readonly_settings["version"])
このコードでは、settings という辞書をもとに readonly_settingsを作っています。readonly_settingsから値を読むことはできます。
では、変更しようとするとどうなるでしょうか。
from types import MappingProxyType
settings = {
"debug": False,
"version": "1.0.0",
}
readonly_settings = MappingProxyType(settings)
readonly_settings["debug"] = True
このコードを実行すると、エラーになります。MappingProxyType で作ったオブジェクトには、直接代入できません。
実務では、設定値や定数のように、他の場所から書き換えてほしくないデータを渡すときに考え方として役立ちます。完全な防御というより、変更してはいけない意図をコードで表すイメージです。
元の辞書を変えると反映される¶
MappingProxyTypeで注意したいのは、元の辞書を変更すると、その変更は反映されるという点です。コピーではなく、元の辞書を参照するビューだからです。
from types import MappingProxyType
settings = {"debug": False}
readonly_settings = MappingProxyType(settings)
print(readonly_settings["debug"])
settings["debug"] = True
print(readonly_settings["debug"])
実行結果は次のようになります。
False
True
readonly_settings自体からは変更できません。ですが、元の settingsを変更すると、readonly_settingsで見える値も変わります。
ここは勘違いしやすいです。外部に見せる側を読み取り専用にしたいのか、元データも含めて完全に固定したいのかを分けて考えましょう。
FunctionTypeで関数かどうかを調べる¶
types モジュールには FunctionType という名前もあります。これは、ユーザーが定義した関数の型を表します。
関数かどうかを調べたいときに使えます。
from types import FunctionType
def hello():
print("hello")
print(type(hello))
print(isinstance(hello, FunctionType))
実行結果は次のようになります。
<class 'function'>
True
helloは関数なので、FunctionTypeと判定されます。isinstanceを使うと、指定した型のオブジェクトかどうかを確認できます。
ただし、実務では関数かどうかの判定にはcallableを使うことも多いです。callableは、呼び出せるオブジェクトかどうかを調べます。
def hello():
print("hello")
class Greeter:
def __call__(self):
print("hi")
greeter = Greeter()
print(callable(hello))
print(callable(greeter))
実行結果はどちらも Trueです。関数だけでなく、__call__を持つオブジェクトも呼び出せるからです。
FunctionTypeは関数そのものを見たいとき、callableは呼び出せるかどうかを見たいとき、と考えると整理しやすいです。
LambdaTypeの使い方¶
LambdaTypeは、lambdaで作られた関数の型です。実は、通常の関数とlambdaはかなり近い存在です。
次のコードを見てみましょう。
from types import FunctionType, LambdaType
def add_one(x):
return x + 1
add_two = lambda x: x + 2
print(isinstance(add_one, FunctionType))
print(isinstance(add_two, LambdaType))
print(FunctionType is LambdaType)
実行すると、どちらも関数として扱われることがわかります。FunctionTypeとLambdaTypeは同じ型として扱われます。
lambda は短い関数をその場で書きたいときに便利です。 ですが、初心者のうちは無理に使いすぎる必要はありません。
読みやすさを優先するなら、defで名前を付けた関数にしたほうがよい場面も多いです。実務では、短く書けることより、あとから読んで理解しやすいことのほうが大切です。
【関連記事】ラムダ式(無名関数)を使いこなす。コードを極限までシンプルにする方法を解説
ModuleTypeでモジュールかどうかを確認する¶
ModuleTypeは、モジュールの型を表します。モジュールとは、Pythonファイルや標準ライブラリのように、機能をまとめたものです。
たとえば、mathモジュールを調べてみましょう。
import math
from types import ModuleType
print(type(math))
print(isinstance(math, ModuleType))
実行結果は次のようになります。
<class 'module'>
True
mathはモジュールなので、ModuleTypeと判定されます。普段はあまり直接使いませんが、動的に読み込んだものがモジュールかどうか確認したい場面で役立ちます。
たとえば、プラグイン機構や自作ツールで外部ファイルを読み込むような設計では、ModuleTypeの考え方が出てくることがあります。初心者のうちは、モジュールにも型があるのだと知っておくだけでも十分です。
NoneTypeの使い方¶
NoneType は、Noneの型を表します。Python 3.10以降ではtypes.NoneTypeとして使えます。
Noneは値がないことを表す特別なオブジェクトです。空文字や0とは違います。
from types import NoneType
value = None
print(type(value))
print(isinstance(value, NoneType))
実行結果は次のようになります。
<class 'NoneType'>
True
ただし、Noneかどうかを判定するだけなら、通常はis Noneを使います。
value = None
if value is None:
print("値がありません")
これはPythonでとてもよく使われる書き方です。NoneTypeを直接使う場面は多くありません。
エンジニア歴10年の現場感としても、None判定はis Noneが基本です。NoneTypeは、型をプログラム上で明示的に扱う必要があるときだけ出番があります。
【関連記事】Noneはただの空ではない。Pythonに1つしか存在しないシングルトンの正体とは?
typesとisinstanceを組み合わせる¶
types モジュールの型名は、isinstanceと組み合わせると理解しやすいです。isinstanceは、オブジェクトが特定の型かどうかを調べる関数です。
from types import FunctionType, ModuleType
import math
def greet():
print("hello")
print(isinstance(greet, FunctionType))
print(isinstance(math, ModuleType))
print(isinstance(123, FunctionType))
実行結果は次のようになります。
True
True
False
greetは関数、mathはモジュール、123は関数ではありません。このように、typesにある型名を使うと、Python内部のオブジェクトの種類を確認しやすくなります。
ただし、型判定に頼りすぎるとコードが硬くなることもあります。Pythonでは、実際に必要な操作ができるかを重視する考え方もあります。
たとえば、関数型かどうかよりも、呼び出せるかどうかが重要ならcallableを使ったほうが自然です。どの判定が目的に合っているかを考えることが大切です。
typesを使わないほうがよい場面¶
typesは便利ですが、何でもtypesで解決しようとする必要はありません。初心者のうちは、むしろ基本機能で書いたほうが読みやすいことも多いです。
たとえば、値が文字列かどうかを確認したいなら、typesは不要です。
name = "Taro"
print(isinstance(name, str))
strは組み込み型なので、そのまま使えます。int、list、dict なども同じです。
また、None判定なら次のように書きます。
value = None
if value is None:
print("値がありません")
このように、普段のコードでは組み込み型や標準的な書き方で十分なことが多いです。typesは、標準の名前では直接扱いにくい型や、少し特殊なユーティリティを使いたいときに登場します。
typesとtypingの違い¶
Pythonで型の話を調べていると、typesだけでなくtypingも出てきます。ここはかなり混乱しやすいポイントです。
typesは、実行中のPythonオブジェクトの型や型関連ユーティリティを扱います。typingは、コードを書くときに型ヒントを付けるために使います。
次の例を見てみましょう。
from typing import Callable
def run_task(task: Callable[[], None]) -> None:
task()
このコードのCallableは、task が呼び出せるものだと示す型ヒントです。実行時に関数かどうかを判定しているわけではありません。
一方で、types.FunctionType は実行中のオブジェクトが関数型かどうかを確認するときに使えます。
from types import FunctionType
def hello():
print("hello")
print(isinstance(hello, FunctionType))
ざっくり言うと、typingはコードを読む人や型チェッカーに意図を伝えるためのものです。typesは実行時に型そのものを扱うためのものです。
まとめ¶
Pythonの types モジュールは、型に関する便利な道具をまとめた標準ライブラリです。 最初は少し難しく見えますが、SimpleNamespaceやMappingProxyTypeから触れると実用イメージがつかみやすくなります。
SimpleNamespaceは、属性アクセスできる簡単なオブジェクトを作りたいときに便利です。テスト用データや小さな設定オブジェクトを作る場面で役立ちます。
MappingProxyTypeは、辞書を読み取り専用のように見せたいときに使えます。ただし、元の辞書を変更すると反映されるため、完全な不変データとは違う点に注意しましょう。
FunctionType、LambdaType、ModuleType、NoneType などを知ると、Pythonでは関数やモジュール、Noneにも型があることが見えてきます。これはPythonのオブジェクト指向的な仕組みを理解するうえで、とても良い入口になります。
初心者のうちは、typesを丸暗記する必要はありません。まずは type、isinstance、callable と組み合わせながら、Pythonの型の世界に少しずつ慣れていきましょう。
ここまでお読みいただきありがとうございました。