Python処理の分離術|ChatGPTで学ぶ「main.py+機能別スクリプト」構成でラクになる開発フロー

AI活用術

🪛 「なんかこのコード…育たないな」って思ったことありませんか?

製品開発の現場でPythonコードを書いていると、最初は動かすことに必死で、 全部1ファイルに詰め込んでた時期がありました。

mainpyにすべてを詰め込んでいた

──とりあえず動く。 でも、実験が進むにつれてバージョンが増え、 条件が増え、データが増え、 「どこを直せばいいか分からない」スパゲッティコードが出来上がっていく。

しかもそれを見せながらレビューに出ると──

「この前処理、他でも使うんだけど再利用できる?」
「モデルだけ変えたいとき、どうすればいい?」
「この評価の数値って、どのロジックで出したの?」

……正直、詰みました。

💥 コードは動いてる。けど、“他人にとって使いにくい”。

「main.pyが重い…」
「コピーして別スクリプトにしたけど、あとから差分が追えない…」
「関数化してなかったから、一部だけ実行ができない…」

そんな“地味だけど積もるストレス”に悩んでいたとき、 ChatGPTとのやりとりで提案されたのが、 **「責務ごとに.pyを分ける構成」**でした。

pythonコードがごちゃごちゃしすぎている

📌 この記事で得られること

この記事は、こんなPython開発の“もやもや”に心当たりがある方に向けて書いています:

  • 気づけば1ファイルに全部詰め込んでて、後から見返すと「どこが何やってんの?」状態
  • 前処理・モデル・評価が全部つながってて、分けたくてもどこから手を付けていいかわからない
  • 別スクリプトでも同じ処理を使いたいけど、毎回コピペで再利用性ゼロ
  • 「この処理だけもう一回試したい」が main.pyごと起動になっていて地味に面倒

──全部、かつての私です。

この記事では、そんな状態から脱却するために実践した**「処理の分割構成(preprocessing.py / modeling.py / evaluate.py / utils.py)」の考え方と実装例**を、リアルな開発現場での経験をもとに解説します。

さらに、ChatGPTと一緒に整理していく中で気づいた
**「main.pyは“司令塔”、他の.pyは“演奏パート”」**という設計思想にも触れながら、
可読性・再利用性・レビュー性を一気に上げる方法を紹介していきます。

🤖 もちろん、main.pyでのサンプルコードもお渡しします!
最後のほうに全部まとめて記載しておきます。

📂 main.pyも活用して全体の再現性・保守性を良くしたい方は👇

現場でたどり着いた「分けるだけで整う」ファイル構成

処理がごちゃっと1ファイルに詰まっていた頃、
「どこから読めばいいのか分からない」「前処理だけ再実行したいのに全部動いちゃう」
──そんな状況にモヤモヤしていたある日、私はChatGPTにこう問いかけました。

Hirokichi
Hirokichi

「処理が多すぎて、コードが読みにくくなってきました。どう整理すればいいですか?」

🤖 ChatGPT:

「責務ごとにファイルを分けると、再利用性・保守性・説明しやすさが一気に向上しますよ」

なるほど、責務ごとに分ける──たしかに、全部詰め込み型じゃなくて“役割ごと”に分けることで、構造も頭もすっきりする。

そこで、ChatGPTと壁打ちしながらたどり着いたのが、この構成です👇

chatGPTに聞いたmaipyファイルとステップファイルの使い方

絵にするとこんなイメージです👇

mainファイルと各ステップファイルのイメージ図

この構成にしてからは──

  • 前処理だけ差し替えたいときに、preprocessing.pyだけ直せばOK
  • 学習モデルの切り替えも、modeling.pyだけ見れば済む
  • ロジックが分かれているから、エラー箇所の特定が爆速
  • main.pyが“司令塔”として、全体の見通しが一目でわかる

ChatGPTのこの一言が特に刺さりました👇

「main.pyは“全体をつなぐ指揮者”、他の.pyは“演奏パート”です」

──この発想が、コードの構成を「ただ動かすもの」から「育てて使うもの」に変えてくれました。

preprocessing.py|データ読み込みと前処理は“まとめて外に出す”

ChatGPTに「構成は“役割ごとに分けましょう”」と言われて、
まず最初に切り出したのがこの preprocessing.py でした。

なぜか?

──前処理は地味だけど、どの案件でも毎回やる“最重要パート”だからです。

mainpyに呼び込むdfを定義

でも、これを全部 main に書くと、何度も同じ処理をコピペしたり、後で直したいときに「あれ?どの行だっけ?」となる。
レビュー時にも「前処理の中身ってどこ?」と聞かれる始末…。

load_dataとmainpyを使い分ける

💡 ここで得られたメリット

  • main.pyが一気にスッキリして、読みやすくなった
  • 前処理だけ再利用できるようになって、検証や別プロジェクトにも応用しやすくなった
  • エラーやバグの調査範囲が明確になった(=前処理で止まったら、このファイルだけ見ればOK)
Hirokichi
Hirokichi

「データの読み込みと前処理、どう使い分ければいいですか?」

🤖 ChatGPT:

「load_data() は“ファイルの入口”、preprocess() は“現場に渡す前の整形”。目的を分けると構成が自然になります」

modeling.py|モデルの学習と保存は“ひとつの箱”にまとめよう

前処理を切り出してスッキリした私は、次に ChatGPT にこう聞きました。

Hirokichi
Hirokichi

「モデルの学習部分も整理したいんですが、どこまでをひとつにすべきですか?」

🤖 ChatGPT:
「学習と保存までをひとつの関数セットにしてまとめると、再利用性と構造の見通しが良くなりますよ」

──ということで、ChatGTPと一緒にmodeling.py を作成しました。

modelingpyの概要

💡 ここがポイント

  • モデルの種類を変えるときに、main.pyをいじらなくていい
     → train_model() の中だけ直せばOK
  • 保存処理もまとめてあるので、実行の流れが自然
     → 「学習したら保存する」の流れが main.py に明快に表れる
  • あとからのチューニングや実験の繰り返しが圧倒的にラク
     → 複数モデルのABテストにも応用しやすい

train_model() をパッケージのように扱うことで、main.py の中に「処理フローのストーリー」を書けるようになる。

そして学習ロジックの中身は modeling.py にお任せ。
この構成にしてから、説明もレビューも爆速になりました。

evaluate.py|評価は“あとから何度でも”できるようにしておく

モデルができたら、次にやるのは「ちゃんと使えるか?」の確認。

でも、評価指標の計算って──
実はあとから何度も見直したくなるものなんです。

私も最初は、学習コードの下に直接こう書いていました👇

chatGPTに聞く前の私のmodelingypy

でも、パラメータを変えるたびに同じ処理をコピペしては実行…
あとから「他の指標も見たい」となって書き足して…
main.pyが地味にぐちゃってくる。

そこで、ChatGPTにこう聞きました。

Hirokichi
Hirokichi

「評価だけ何度も試したいんですけど、これも分けたほうがいいですか?」

🤖 ChatGPT:
「評価処理は関数化&ファイル分離しておくと、再実行しやすく、mainもスリムになります」

というわけで evaluate.py を作りました👇

chatGPT_maipyとmodeligpy

💡 この構成のいいところ

  • main.py の末尾に「評価」ってひと目でわかる
  • あとから指標を追加・変更しやすい(F1やR²などもサクッと追加可能)
  • 評価処理だけ別データで再実行できる(=実験パターンが増えても柔軟)

ChatGPTの言葉を借りると──

「評価は“何がよかったか”を説明するための材料。だからこそ、独立させておく価値があります」

このアドバイスも、地味に沁みました。

評価だけを“独立パート”にしておくと、main.py にも余計なロジックが混ざらないし、
レビュー時にも「あ、この関数で評価したんですね」と一発で伝わる。

utils.py|“よく使うやつ”は道具箱にまとめとけ

開発を進めていると、だんだんこういう関数が増えてきませんか?

  • 出力フォルダがなければ作るやつ
  • ファイル名をいい感じに生成するやつ
  • 複数のパスを結合するやつ

…いわば「道具として何度も使うやつ」です。

私も最初は、そういう処理を毎回 .py の先頭や末尾にベタ書きしてました。

utilspyの使い方をchatgptに聞く前

──気づけば同じコードが、3箇所のスクリプトに散らばってる。

Hirokichi
Hirokichi

「「この ‘os.makedirs’、いろんなファイルに書いてるんですが、まとめた方がいいですか?」

🤖 ChatGPT:

「そのような汎用処理は ‘utils.py’ に集約すると、再利用性が高まり保守も楽になります」

たしかに、**「どこでも使うもの=ひとつにまとめる」**は鉄則。

というわけで、私は utils.py という“道具箱”を用意しました👇

utilspyの使い方をchatgptに聞いた後

💡 これが意外と便利だった理由

  • 複数のスクリプトで同じ機能を使い回せる
  • 1箇所にあるから修正も1回で済む
  • ファイル構成を見たときに「雑用はここ」ってわかる

ChatGPTいわく:

「“utils”は目立たないけど、プロジェクト全体の“骨組み”になります」

──ほんとその通りでした。

あとから「フォルダ構成を変えたい」「ファイルの命名ルールを調整したい」ってなっても、
utilsだけ触れば全部に反映されるので、保守コストが爆下がりします。

📂 そもそも可読性を上げる意味とは?を知りたい方はこちらへ👇

まとめ|“処理を分ける”だけで、コードは見違える

正直、最初の頃の私は「全部まとめて1ファイルで書けば早くね?」と思ってました。

でも実際には──

  • どこに何があるかわからない
  • 同じ処理を何度も書いてしまう
  • バグ修正や追加実装のとき、手がつけられなくなる

という“地味なストレス”が蓄積されていったんです。

そこに、ChatGPTとのやり取りを通じて学んだ考え方が刺さりました👇

「main.pyは司令塔。他の.pyは役割ごとの演奏パート」

この発想でコードを分けてみたら、 **main.py は“読むだけで処理の全体像がわかる”**し、
**それぞれの.pyは“必要なときにだけ開けばいい”**ようになったんです。


ここまで分けた私の構成(再掲)

chatGPTに聞いたmaipyファイルとステップファイルの使い方

この構成にしてからは、明らかにこう変わりました:

  • 「ここだけ実行したい」がすぐできる
  • 「どこに何があるか」の説明が一瞬で済む
  • 「あとでまた使いたい」がスムーズになる

そしてなにより──
コードを自分の手で“育ててる”感覚が生まれたんです。

🧾 load_data.py(※前処理と別に読み込み処理を切り出す場合)

import pandas as pd

def load_data(path):
    return pd.read_csv(path)

🧼 preprocessing.py

def preprocess(df):
    df = df.dropna()
    df = df[df["value"] >= 0]  # 負の値を除去
    X = df.drop("target", axis=1)
    y = df["target"]
    return df, X, y

🤖 modeling.py

from sklearn.linear_model import LogisticRegression
import joblib

def train_model(X, y):
    model = LogisticRegression()
    model.fit(X, y)
    return model

def save_model(model, path):
    joblib.dump(model, path)

📊 evaluate.py

from sklearn.metrics import mean_absolute_error

def evaluate_model(model, X, y):
    preds = model.predict(X)
    mae = mean_absolute_error(y, preds)
    return {"MAE": mae}

🛠 utils.py

import os

def ensure_dir(path):
    if not os.path.exists(path):
        os.makedirs(path)

🚀 main.py

import logging
from datetime import datetime

from load_data import load_data  # または preprocessing.py に含まれる場合は不要
from preprocessing import preprocess
from modeling import train_model, save_model
from evaluate import evaluate_model
from utils import ensure_dir

# ログ設定
logging.basicConfig(
    filename='output.log',
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(message)s'
)

def main():
    logging.info("処理を開始します")
    logging.info(f"開始時刻: {datetime.now()}")

    # データ読み込み
    try:
        df = load_data("data/input.csv")
        logging.info("データの読み込みが完了しました")
    except Exception as e:
        logging.error(f"データの読み込みに失敗しました: {e}")
        return

    # 前処理
    df_processed, X, y = preprocess(df)
    logging.info("前処理が完了しました")

    # モデル学習
    model = train_model(X, y)
    logging.info("モデルの学習が完了しました")

    # モデル保存
    save_model(model, "models/model.pkl")
    logging.info("モデルの保存が完了しました")

    # 評価
    metrics = evaluate_model(model, X, y)
    logging.info(f"評価結果: {metrics}")

    logging.info("処理が正常に終了しました")

if __name__ == "__main__":
    main()

📣 最後にひとこと:

誰かに渡すコードじゃなくても、
「未来の自分に渡すコード」だと思って、分けておくのがおすすめです。

ChatGPTとの壁打ちで気づいたことはたくさんありますが、
“構造を整える”だけで、開発はこんなにラクになるんだということが、一番大きな発見でした。

次回も”現場でのリアルな体験”を基に、記事を書いていきたいと思います!

コメント

タイトルとURLをコピーしました