第114回 実務で使えるPythonスクリプト設計:関数・モジュール・標準ライブラリで作る再利用可能な自動化

業務で「ちょっと自動化したい」場面は多いのに、作ったスクリプトがすぐ壊れたり、別の仕事で使い回せなかったりして疲れていませんか?本記事は、実務で使える「読みやすく、再利用でき、運用しやすい」Pythonスクリプトの設計とテンプレートを、現場目線で整理します。まずは小さな改善から始められるように、チェックリストとそのまま使える雛形を提供します。

なぜスクリプト構造が重要か(保守性・テスト・再利用)

短時間で動くスクリプトを書けても、継続的に運用するには設計が必要です。理由は主に以下のとおりです。

  • 保守性:誰か(自分含む)が手直ししやすい構造にする
  • テスト性:単体関数に分ければ自動テストが書きやすい
  • 再利用性:共通処理をモジュール化して別プロジェクトで再利用できる
問題 影響 改善策
ワンファイルで処理が直列化 変更時に影響範囲が分かりにくい 関数分割・モジュール化・明確な入出力
環境依存の設定が直書き 他環境で動かない、テスト困難 環境変数・設定ファイルで分離

最低限のプロジェクトレイアウト

簡潔で運用しやすい推奨レイアウトを示します。必要に応じて拡張してください。

パス 目的
scripts/ 実行用スクリプト(cronやsystemdで使う)
src/your_package/ 再利用するモジュール・ビジネスロジック
tests/ ユニットテスト
requirements.txt / pyproject.toml 依存管理
config/ or .env 環境ごとの設定

関数設計の実務(単一責任・入出力を明確に)

関数は「何を受け取り、何を返すか」を明確にします。サイドエフェクト(ファイル書き込み、外部API呼び出し)は最小化し、必要なら別関数に分離します。

設計観点 チェック項目
単一責任 1関数=1目的。入出力が増える場合は分割を検討
純粋関数優先 副作用を分離(例:データ処理と保存を別関数に)
明確な例外処理 例外の種類を限定し上位でハンドルする

モジュールとパッケージ化(__main__ の使い方・importの設計)

エントリポイントは scripts/ に置くか、パッケージの __main__.py を使います。ライブラリ部分は src/ 以下に切り出してテストと再利用を容易にします。

パターン 目的・使い方
if __name__ == “__main__” スクリプト実行時にのみ起動する初期化やCLI接続をここに置く
src/your_package/api.py 外部呼び出しラッパーやビジネスロジックを配置

CLI化:argparseでの引数設計とヘルプ

ユーザが使いやすいCLIは引数設計が肝心です。必須・任意・デフォルトを明確にし、helpを丁寧に書きます。

引数 用途
–config 設定ファイルのパス –config config/prod.json
–dry-run 動作確認用(変更は加えない) –dry-run
–log-level ログ出力レベル –log-level INFO

ユーザ向けヘルプのコツ

  • 短く何をするかを書き、例を1つ載せる
  • 重要な引数は必須にして、デフォルトは説明する

標準ライブラリの実務的な使い方

標準ライブラリをきちんと使うと依存を減らし、長期運用が楽になります。以下に実務でよく使うモジュールと用途をまとめます。

モジュール 実務的な使い方
pathlib OSに依存しないパス操作。ファイルの存在チェックや作成に便利
logging 運用ログ。ハンドラ分離(コンソールとファイル)、ログ回転はlogging.handlersを使用
os / dotenv / environ 機密情報や環境差分は環境変数で管理。小規模なら .env を使う
json / csv シリアライズ、データ交換。utf-8での入出力に注意
datetime UTCベースで管理、フォーマットはISO 8601推奨

AI(LLM)連携の実例設計と運用パターン

AI API呼び出しは外部依存のため、堅牢なラッパーを作り、リトライ・検証・ログ記録を行います。ここでは設計パターンと簡単な雛形を示します。

設計要素 実務ポイント
ラッパー関数 APIキーやエンドポイントは引数化/環境変数化。レスポンスの基本チェック(ステータス、スキーマ)を行う
リトライとバックオフ 短時間の再試行は内製、指数バックオフを実装。10回など過剰なリトライは避ける
レスポンス検証 必要項目が揃っているか確認し、不正ならエラーを返す

ラッパーの雛形(概念)

以下はコードの雛形をそのまま貼れる形で示します(簡潔化しています)。必要に応じて HTTP クライアントや認証方法を置き換えてください。

simple_ai_wrapper.py
def call_ai_api(payload, endpoint, api_key, retries=3, backoff=2):
    """シンプルなリトライとレスポンス検証の例
    - payload: dict
    - endpoint: str
    - api_key: str
    """
    import time, requests
    for attempt in range(1, retries + 1):
        resp = requests.post(endpoint, json=payload, headers={"Authorization": f"Bearer {api_key}"}, timeout=30)
        if resp.status_code == 200:
            data = resp.json()
            # 必要な項目チェック例
            if "choices" in data:
                return data
            raise ValueError("Unexpected response structure")
        if attempt == retries:
            resp.raise_for_status()
        time.sleep(backoff ** attempt)

運用に向けた実践チェックリスト

デプロイ前にチェックしておきたい項目を一覧にします。SOP(標準作業手順書)への落とし込みをおすすめします。

カテゴリ 項目
環境 仮想環境(venv/poetry)と requirements.txt/pyproject の整備
起動方法 cron/systemd 用の起動スクリプトとログの標準化
監視 ログ出力(レベル別)、エラー通知(メール/Slack)設定
リカバリ 失敗時の再試行ルールと手動復旧手順の記載
ドキュメント 使用方法とSOPを README と別に用意

付録:コピーして使えるスクリプト雛形

この雛形は、argparse + logging + config読み込み + LLM呼び出しラッパーの最小セットです。適宜置き換えて使ってください。

template_script.py
import argparse
import logging
import json
from pathlib import Path
import os

# 設定読み込み(JSONの例)
def load_config(path):
    p = Path(path)
    with p.open("r", encoding="utf-8") as f:
        return json.load(f)

# シンプルなログ設定
def setup_logging(level):
    logging.basicConfig(level=level, format="%(asctime)s %(levelname)s %(message)s")

# AIラッパー(外部ファイルに分けることを推奨)
def call_ai_api(payload, endpoint, api_key, retries=3):
    import time, requests
    for i in range(1, retries+1):
        resp = requests.post(endpoint, json=payload, headers={"Authorization": f"Bearer {api_key}"}, timeout=30)
        if resp.status_code == 200:
            return resp.json()
        if i == retries:
            resp.raise_for_status()
        time.sleep(2 ** i)

# 処理の主体(入出力は引数化)
def process(data_path, config):
    p = Path(data_path)
    # ファイル読み込み・処理をここに記述
    return {"status": "ok"}

def main():
    parser = argparse.ArgumentParser(description="小さなAI呼び出し自動化スクリプト雛形")
    parser.add_argument("data_path", help="処理対象ファイルのパス")
    parser.add_argument("--config", default="config/prod.json", help="設定ファイルのパス")
    parser.add_argument("--log-level", default="INFO", help="ログレベル")
    parser.add_argument("--dry-run", action="store_true", help="変更を加えないで実行")
    args = parser.parse_args()

    setup_logging(args.log_level)
    cfg = load_config(args.config)

    logging.info("開始: %s", args.data_path)
    result = process(args.data_path, cfg)
    logging.info("完了: %s", result)

if __name__ == "__main__":
    main()

よくある落とし穴と回避策

  • 直接AWSキーなどをソースに書かない:環境変数やシークレットマネージャを使う
  • ログが冗長で必要な情報が埋もれる:ERROR/WARNは必ず人が見られるようにする
  • リトライのしすぎ:外部APIに負荷をかけないよう指数バックオフと上限を設定

他記事との連携と次の一歩

ファイル入出力やCSV処理のベストプラクティスは第113回で、テストやCIは第94回で扱っています。次回は並列実行とジョブキューを取り上げ、運用での安全性と拡張性を検討します。

まとめ

本記事では、実務で使えるPythonスクリプトを設計する際の考え方とテンプレートを示しました。ポイントは関数を小さく保ち、設定と実行を分離し、標準ライブラリを有効活用することです。付録の雛形をコピーして、まずは小さな自動化から運用に乗せてみてください。

成果物:記事を読んだらすぐ動かせるサンプルリポジトリ(小さなAI呼び出し自動化スクリプト)を用意しています。次のステップとして、並列実行とジョブ管理の導入を検討してください。