Skip to content
This page has been auto-translated and may contain errors.View in English

モジュールと標準ライブラリ

Python には、すぐに使える膨大なツール群が同梱されています。乱数、数学、日付、ファイルパスなど、その他多数です。これらのツールは モジュール に収められており、import を使ってコードに取り込みます。前章で import json をすでに使いましたね。本章では import を完全に解説し、標準ライブラリの最も便利な部分を紹介します。

Python の標準ライブラリは、よくある問題に対するテスト済みで文書化された解決策を提供します。モジュールはコードを整理する単位で、各ファイルがモジュール、__init__.py を持つ各ディレクトリがパッケージです。import システムはモジュールを見つけ、必要に応じてコンパイルし、sys.modules にキャッシュするので、一度だけ読み込まれます。

import システムは階層化された仕組みです。finder がモジュールを探し、loader がコンパイルして実行します。結果は sys.modules にキャッシュされます。import foofoo.py を一度実行し、モジュールオブジェクトを現在の名前空間の foo に束縛します。from foo import barbar のみを束縛します。sys.path__init__.py、相対 import を理解することは、パッケージを構築するうえで必須です。

モジュールのインポート

最もシンプルな import は、モジュール全体を取り込み、ドット記法でその内容を使えるようにします。特定の名前だけをモジュールからインポートすることもでき、その場合は接頭辞なしで直接使えます。エイリアスを使えば長い名前を短くできます。

import module は、モジュールオブジェクトを現在のスコープの名前 module に束縛します。from module import namename のみを束縛します。エイリアス (import module as alias) はサードパーティライブラリでよく使われます。from module import * は避けましょう。名前空間を汚染し、名前の由来が分かりにくくなります。

import module は完全な import 機構を起動し、結果を sys.modules にキャッシュしてモジュールオブジェクトを束縛します。from module import name は構文糖衣で、内部ではモジュール全体をインポートしてから name を取り出します。循環 import はよくある落とし穴で、解決策は通常 import を関数の中に移動するか、モジュールの依存関係を再構成することです。importlib.import_module() を使えばプログラム的に import できます。

python
import math

math.sqrt(16)     # 4.0
math.pi           # 3.141592653589793
math.floor(3.9)   # 3
math.ceil(3.1)    # 4

モジュールから特定の名前をインポートして、直接使う:

python
from math import sqrt, pi

sqrt(16)    # 4.0 ("math." 接頭辞は不要)
pi          # 3.141592653589793

モジュールや名前にエイリアスを付けて短くする:

python
import math as m

m.sqrt(16)    # 4.0

from math import sqrt as square_root
square_root(25)    # 5.0

エイリアスは人気のサードパーティライブラリ (import numpy as npimport pandas as pd) でよく使われます。標準ライブラリのモジュールでは、フルネームを使うほうがコードが読みやすくなります。

random

random モジュールは乱数を生成し、ランダムな選択を行います。ゲーム、シミュレーション、ランダムサンプリングなど、予測不能性が必要なあらゆる場面で使えます。シード を設定すると結果が再現可能になります。同じシードからは毎回同じ系列が生成されます。

random はメルセンヌ・ツイスタ疑似乱数生成器を使います。シードが系列全体を決定し、同じシードからは常に同じ出力が得られます。.choice() は 1 要素を選び、.choices() は重複ありで選び、.sample() は重複なしで選びます。.shuffle() はリストをインプレースで変更し None を返します。

random は 624 ワード状態のメルセンヌ・ツイスタ (MT19937) PRNG を使います。random.seed() は状態を初期化します。指定しない場合、状態は os.urandom() からシードされます。暗号用途には代わりに secrets を使いましょう。random は暗号学的に安全ではありません。random.SystemRandom() は同じ API で os.urandom() をラップした安全な代替を提供します。

python
import random

random.random()              # 0 以上 1 未満の float
random.randint(1, 10)        # 1 から 10 までの整数 (両端含む)
random.uniform(1.0, 10.0)    # 1.0 から 10.0 の間の float

colours = ["red", "green", "blue"]
random.choice(colours)       # 1 要素を選ぶ
random.choices(colours, k=3) # k 個選ぶ (重複あり)
random.sample(colours, k=2)  # k 個選ぶ (重複なし)

numbers = [1, 2, 3, 4, 5]
random.shuffle(numbers)      # インプレースでシャッフル、None を返す

再現可能な結果が必要な場合 (テストやデータサイエンスで便利)、生成前に シード を設定します:

python
random.seed(42)
random.randint(1, 100)   # シード 42 では常に同じ値

同じシードからは、どのマシンでも毎回同じ系列が生成されます。

math

math モジュールは、基本的な算術演算子を超える、より高度な数学演算を追加します。平方根、べき乗、対数、三角関数、そして pi や無限大などの特殊値もここにあります。

math は標準的な数学関数の C レベル実装を提供します。math.pow() は常に float を返しますが、Python の ** 演算子は整数の底と指数の場合 int を返すことに注意してください。math.log(x, base) は任意の底の対数を計算し、math.log(x) は自然対数を計算します。

math は C の <math.h> ライブラリ関数をラップしています。これは純粋な Python 実装より高速で、エッジケース (NaN、無限大) も正しく処理します。math.isnan()math.isinf() は IEEE 754 の特殊値をチェックします。複素数には cmath が対応する関数を提供します。配列レベルの数学には numpy が標準的なツールです。

python
import math

math.sqrt(25)        # 5.0
math.pow(2, 10)      # 1024.0 (2 ** 10 と同じだが常に float を返す)
math.log(100, 10)    # 2.0 (底 10 の log)
math.log(math.e)     # 1.0 (自然対数)

math.sin(math.pi / 2)   # 1.0
math.cos(0)             # 1.0

math.ceil(3.2)    # 4
math.floor(3.9)   # 3
math.trunc(3.9)   # 3 (正の値では int() と同じ)

math.inf          # 無限大
math.isnan(float("nan"))   # True
math.isinf(math.inf)       # True

datetime

datetime モジュールは日付と時刻を扱います。datetime.now() は現在の日付と時刻を返します。strftime() は文字列としてフォーマットします。strptime() は文字列を datetime に変換します。timedelta は加算・減算できる期間を表します。

datetimedatetimedelta が主要なクラスです。strftime() はフォーマットコードを使って datetime を文字列にフォーマットします。strptime() はフォーマットパターンを指定して文字列を解析します。timedelta は算術演算をサポートしており、日付に期間を足したり引いたり、<>- で datetime を比較したりできます。

datetime オブジェクトはデフォルトでナイーブ (タイムゾーンなし) です。タイムゾーン対応の datetime には、datetime.now(tz=timezone.utc) または datetime.fromisoformat() をオフセット付きで使います。strftime/strptime は C ライブラリのフォーマットコードを使い、%f はマイクロ秒を表します。高精度な計時には datetime.now() よりも time.perf_counter() を選びましょう。zoneinfo モジュール (Python 3.9+) は IANA タイムゾーンをサポートします。

python
from datetime import datetime, date, timedelta

now   = datetime.now()           # 現在の日付と時刻
today = date.today()             # 現在の日付のみ

print(now.year, now.month, now.day)
print(now.hour, now.minute, now.second)

# フォーマット
print(now.strftime("%Y-%m-%d"))           # "2024-01-15"
print(now.strftime("%d %B %Y, %H:%M"))   # "15 January 2024, 09:42"

# 解析
deadline = datetime.strptime("2024-12-31", "%Y-%m-%d")

# 算術演算
tomorrow    = today + timedelta(days=1)
next_week   = today + timedelta(weeks=1)
diff        = deadline - now
print(f"{diff.days} days until deadline")

よく使われる strftime コード:

コード意味
%Y4 桁の年2024
%m月 (ゼロ埋め)01
%d日 (ゼロ埋め)15
%H時 (24時間)09
%M42
%B月の完全な名前January

os と pathlib

pathlib はファイルパスを扱う現代的な方法です。Path オブジェクトを使うと、/ 演算子でパスを構築・検査・ナビゲートできます。os は環境変数や低レベルの OS 操作にアクセスできます。新しいコードでは pathlib を優先しましょう。

pathlib.Path はファイルシステムのパスを、クエリやナビゲーション用のメソッドを持つオブジェクトとして表現します。/ 演算子はパスのコンポーネントをきれいに連結し、OS 固有のセパレータを自動で処理します。os.environ は環境変数の辞書ライクなオブジェクトです。os.environ.get("KEY", "default") は存在しない変数に対しても安全です。

pathlib.Path は抽象基底クラスで、PurePosixPathPureWindowsPath が各 OS の具体実装です。.glob().rglob().iterdir() のようなメソッドはジェネレータを返します。.stat()os.stat() を呼び出し stat_result を返します。os.path の関数は Python 3.6 以降、文字列と Path オブジェクトの両方を受け付けます。新しいコードでは pathlib を優先し、Path を受け付けない API を呼び出すときには os.fspath()Pathstr に変換しましょう。

python
from pathlib import Path

p = Path("data/reports")

p.exists()           # パスが存在すれば True
p.is_dir()           # ディレクトリなら True
p.is_file()          # ファイルなら True

p.mkdir(parents=True, exist_ok=True)   # ディレクトリを作成

for f in p.glob("*.csv"):              # ディレクトリ内のすべての CSV ファイル
    print(f.name)                      # ファイル名のみ

report = p / "report_jan.csv"          # / 演算子でパスを連結
report.stem       # "report_jan" (拡張子なしの名前)
report.suffix     # ".csv"
report.parent     # Path("data/reports")

content = report.read_text()           # ファイル内容を直接読み込む
report.write_text("new content\n")    # 直接書き込む

os モジュール:

python
import os

os.getcwd()                        # 現在の作業ディレクトリ
os.listdir(".")                    # ディレクトリの中身を一覧
os.path.exists("data.txt")        # パスが存在すれば True
os.path.join("data", "file.txt")  # "data/file.txt" (クロスプラットフォーム)
os.environ.get("HOME")            # 環境変数を読み取る

新しいコードでは pathlib を優先しましょう。環境変数が必要な場合や、文字列を期待する古い API を扱う場合は os を使います。

timeit

timeit はコードの実行時間を計測します。2 つの手法を比較してより速いほうを選びたいときに便利です。安定した計測値を得るには、コードを何回も実行します。

timeit.timeit(stmt, setup, number)stmtnumber 回実行して、経過時間の合計を秒単位で返します。setup の文字列は計測ループの前に一度だけ実行されます。結果を number で割れば、1 回あたりの時間が得られます。繰り返し回数を増やせば、システムのスケジューリングによるノイズが減ります。

timeit は計測中にガベージコレクタを無効化してノイズを減らします。高解像度な計測には time.perf_counter() を使います。globals パラメータは計測対象の文に名前空間を渡します。マイクロベンチマークには timeit が標準的なツールですが、大きなプログラム内でどこに時間が使われているかを調べるには cProfile を使いましょう。

python
import timeit

# 単一の文を計測
timeit.timeit("sum(range(1000))", number=10000)

# より複雑なブロックを計測
setup = "data = list(range(1000))"
code  = "[x * 2 for x in data]"
time  = timeit.timeit(code, setup=setup, number=10000)
print(f"{time:.4f} seconds for 10,000 runs")

number は繰り返す回数です。繰り返し回数を増やすほど計測値が安定します。

string

string モジュールは、文字、数字、句読点用のあらかじめ用意された文字列定数を提供します。文字をチェックしたり、特定のアルファベットから乱数文字列を生成したりするときに便利です。

string モジュールの定数 (ascii_lettersdigitspunctuation) は普通の文字列で、インデックスアクセス、イテレーション、in での使用が可能です。random.choices() と組み合わせて、ランダムなトークンやパスワードを生成するのが定番の使い方です。

string モジュールの定数は、特別な振る舞いを持たない純粋な Python の文字列リテラルです。集合ではないので in は O(n) です。頻繁にメンバーシップをテストする場合は set(string.digits) を使いましょう。string.Formatterstring.Template は、それぞれ str.format()$ 形式の置換の基盤となる仕組みです。

python
import string

string.ascii_lowercase   # "abcdefghijklmnopqrstuvwxyz"
string.ascii_uppercase   # "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
string.ascii_letters     # 両方を結合したもの
string.digits            # "0123456789"
string.punctuation       # すべての句読点

文字をチェックしたり、ランダムな文字列を生成したりするときに便利です:

python
import string, random

chars    = string.ascii_letters + string.digits
password = "".join(random.choices(chars, k=12))

自分自身のモジュールを作成する

すべての Python ファイルはモジュールです。別のファイルから使うには、ファイル名で (.py を付けずに) インポートします。モジュール全体をインポートしてドット記法で内容を使うこともできますし、特定の名前を直接インポートすることもできます。

Python がモジュールを import すると、ファイルを上から下まで一度実行し、結果を sys.modules にキャッシュします。同じモジュールに対する以降の import は、ファイルを再実行することなくキャッシュされたオブジェクトを返します。大規模なプロジェクトでは、モジュールは パッケージ (__init__.py を持つディレクトリ) として整理されます。

import の解決は sys.path を使います。これは順番に検索されるディレクトリのリストです。sys.path[0] はスクリプトのディレクトリです。PYTHONPATH 環境変数は追加のディレクトリを先頭に追加します。パッケージとして認識されるには __init__.py が必要です (空でも構いません)。相対 import (from . import module) はパッケージ内で有効です。importlib.reload() はモジュールを再実行しますが、既存の古いオブジェクトへの参照は更新されません。

python
# utils.py
def clamp(value, lo, hi):
    return max(lo, min(value, hi))

PI = 3.14159
python
# main.py
import utils

utils.clamp(150, 0, 100)   # 100
utils.PI                    # 3.14159

from utils import clamp
clamp(50, 0, 100)           # 50

Python はインポートするファイルと同じディレクトリ (および他のいくつかの場所) を見てモジュールを探します。大規模なプロジェクトでは、モジュールは パッケージ (__init__.py ファイルを持つディレクトリ) として整理されます。

__name__ == "__main__"

Python がファイルを直接実行すると、__name__"__main__" に設定されます。同じファイルがモジュールとして import されると、__name__ はモジュール名になります。このパターンを使えば、ファイルを直接実行したときに動くが、別のモジュールから import されたときには動かないコードを書けます。

if __name__ == "__main__": は実行可能なモジュールコードに対する標準的なガードです。これにより、モジュールが import 可能 (関数を公開する) でありながら、直接実行可能 (テストやデモコード付き) にもできます。これがないと、モジュールを import するだけでトップレベルのコードが実行されてしまい、ほとんどの場合望ましくありません。

__name__ は import 機構によって設定されます。エントリーポイントスクリプトでは "__main__"、それ以外ではモジュールのドット区切り名です。このガードにより、副作用 (起動コード、引数解析、テスト実行) が import 時に実行されるのを防ぎます。コマンドラインツールでは、エントリーポイントのロジックを main() 関数に入れて、ガード下で呼び出すのが慣例的なパターンです。

python
# utils.py
def clamp(value, lo, hi):
    return max(lo, min(value, hi))

if __name__ == "__main__":
    # これは python utils.py を実行したときだけ動く
    # import utils したときには動かない
    print(clamp(150, 0, 100))   # 100

これは、スタンドアロンスクリプトとしても役立つあらゆるモジュールに対する標準的なパターンです。

標準ライブラリの注目モジュール

知っておく価値があるさらにいくつかのモジュールです。それぞれが、自分で実装しようとするとかなりの労力が必要となる一般的な問題を解決してくれます。

標準ライブラリは膨大です。以下の注目モジュールは、実運用コードで最もよく出会うものです。完全なリファレンスは docs.python.org/3/library が正式な情報源です。

標準ライブラリは、十分にテストされ文書化されたモジュールの厳選セットです。サードパーティパッケージに手を伸ばす前に、標準ライブラリに解決策がないか確認しましょう。functoolsitertoolscontextlibdataclassestypingabc はいずれも、サードパーティパッケージがしばしば再発明するツールを提供しています。

collections: 特殊なコンテナ型:

python
from collections import Counter, defaultdict, deque

Counter(["a", "b", "a", "c", "a"])   # Counter({'a': 3, 'b': 1, 'c': 1})
defaultdict(list)                      # 欠落キーを自動生成する辞書
deque([1, 2, 3], maxlen=5)            # 両端からの append/pop が高速

itertools: イテラブルを扱うツール:

python
import itertools

list(itertools.chain([1, 2], [3, 4]))          # [1, 2, 3, 4]
list(itertools.islice(range(100), 5))          # [0, 1, 2, 3, 4]
list(itertools.combinations([1, 2, 3], 2))     # [(1, 2), (1, 3), (2, 3)]
list(itertools.product([0, 1], repeat=2))      # [(0,0), (0,1), (1,0), (1,1)]

sys: Python インタプリタへのアクセス:

python
import sys

sys.argv        # コマンドライン引数のリスト
sys.exit(1)     # ステータスコードを指定して終了
sys.version     # Python バージョンの文字列

サードパーティパッケージ: 標準ライブラリ以外に、pip でコミュニティパッケージをインストールできます:

bash
pip install requests    # HTTP ライブラリ
pip install pandas      # データ操作
pip install numpy       # 数値計算

サードパーティパッケージは本ガイドの範囲外ですが、パターンは常に同じです: pip install してから import するだけです。

実践例

randomstringdatetime を組み合わせて、タイムスタンプ付きのユニークなゲーム ID を生成する例:

python
import random
import string
from datetime import datetime

def generate_game_id(length: int = 8) -> str:
    chars = string.ascii_uppercase + string.digits
    return "".join(random.choices(chars, k=length))

def timestamp() -> str:
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

game_id = generate_game_id()
print(f"[{timestamp()}] Starting game {game_id}")

scores = [random.randint(50, 100) for _ in range(5)]
print(f"Round scores: {scores}")
print(f"Best: {max(scores)}")

pathlibdatetime を使ってディレクトリ内のファイルを見つけ、そのサイズを報告する例:

python
from pathlib import Path
from datetime import datetime

def find_files(directory: str, pattern: str = "*.csv") -> list[Path]:
    return sorted(Path(directory).glob(pattern))

def timestamp() -> str:
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

files = find_files(".", "*.md")[:3]
print(f"[{timestamp()}] Found {len(files)} file(s)")
for f in files:
    size = f.stat().st_size if f.exists() else 0
    print(f"  {f.name} ({size} bytes)")

環境変数から型付きデフォルト値を持つアプリ設定を読み込み、構造化されたアクセスログエントリを改行区切り JSON として書き出す例:

python
import os
import json
from datetime import datetime
from pathlib import Path

def load_env_config() -> dict:
    return {
        "debug":     os.environ.get("DEBUG", "false").lower() == "true",
        "port":      int(os.environ.get("PORT", "8080")),
        "log_level": os.environ.get("LOG_LEVEL", "INFO"),
    }

def write_access_log(method: str, path: str, status: int) -> None:
    log_dir = Path("logs")
    log_dir.mkdir(exist_ok=True)
    entry = {
        "ts":     datetime.now().isoformat(),
        "method": method,
        "path":   path,
        "status": status,
    }
    with open(log_dir / "access.jsonl", "a") as f:
        f.write(json.dumps(entry) + "\n")

config = load_env_config()
print(f"Starting on port {config['port']}, debug={config['debug']}")
write_access_log("GET", "/users", 200)

改行区切り JSON (.jsonl) はよくあるログ形式です。各行が有効な JSON オブジェクトで、ファイル全体を読み込むことなく、ストリーミング、追記、行単位での解析が容易になります。