CHAPTER 9 ・ AIと一緒に、中級者として / Lesson 34

AIにコードを整理してもらう

動くけれど読みにくいコードを、AIに整えてもらう。頼み方と、変わっていないことの確かめ方です。

目安 22分 ・ インストール不要

このレッスンが終わったら

「リファクタリングは『動きを変えない』と明記して頼む。前後で同じ入力を動かし、結果が一致するか確かめる」と言える。

STEP 1

こんな場面、ありませんか?

前任者が残した集計のコードや、数か月前に自分で書いたコードを開いて、「動いてはいるけれど、読むのがつらい」と感じたことはないでしょうか。たとえば、次のコードです。2025年9月の売上を、得意先ごとに合計し、「万円」の単位(切り捨て)でも並べて表示します。

動くけれど、読みにくい集計コード(整理する前)サンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

結果は正しいのですが、d x a t という名前では、何の数字なのか分かりません。同じ x["得意先コード"] が何度も出てきますし、10000 や "2025-09" の意味も、読み解かないと分かりません。

Lesson 17 では、こうしたコードを自分で書き直しました。この回では、その整理をAIに頼むやり方を学びます。AIは整理が得意ですが、「整理して」とだけ頼むと、見た目と一緒に動きまで変えてしまうことがあります。頼み方と、変わっていないことの確かめ方が大切です。

STEP 2

条件をつけて頼み、返ってきたコードを動かす

「このコードを整理してください」だけでは、何を、どこまで変えてよいのかが、AIには分かりません。そこで、リファクタリング(動きを変えずに、中身だけを整えること)であることを、はっきり書きます。

整理を頼むときの文章(例)

次のPythonコードを、読みやすく整理(リファクタリング)してください。

【やりたいこと】
2025年9月の売上を、得意先コードごとに合計し、万円(切り捨て)でも表示する

【守ってほしいこと】
- 動きを変えない。同じ入力には、表示も含めて同じ出力になること
- 計算のしかた(切り捨て・四捨五入)、並び順、表示の形式は変えない
- 処理を関数に分け、意味の分かる名前を付ける。決め打ちの値は定数にする
- 標準ライブラリだけを使う。エラー処理(try / except)は足さない
- 変えた点を、箇条書きで説明する

【コード】
(ここに、整理したいコードを貼る)

このように頼むと、次のようなコードが返ってきます。動かしてみましょう。

AIの返答の例:整理したコードサンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

画面の結果は、整理する前と同じに見えます。d が totals、a が amount と、名前が意味を持ち、役目ごとに関数も分かれました。

STEP 3

AIが「勝手に変えがち」なことを知る

頼み方を工夫しても、AIが親切のつもりで、動きを変えてしまうことは、なくなりません。整理されたコードを受け取ったら、まず「何が変わったか」を読みます。

整理する前

if x["得意先コード"] in d:
  d[x["得意先コード"]] = d[x["得意先コード"]] + a
else:
  d[x["得意先コード"]] = a

整理した後(AIの返答)

totals[code] = totals.get(code, 0) + amount
4行が1行になりました。.get(code, 0) は、「なければ0から始める」という意味で、if / else の代わりをしています。

こうした書き換えは、問題ありません。気をつけたいのは、次のような見た目はほとんど変わらないのに、結果が変わるものです。

変わりやすいものどう変わるか起きること
丸め切り捨て // が、round() に変わる金額が1ずれる
並び順コード順が、金額の大きい順に変わる表示の順番が変わる
エラー処理try / except が足され、壊れた行を黙って飛ばす間違いに気づけなくなる
列名・表示列名の言い換え、桁区切りの追加後ろの処理が動かなくなる

丸めの違いを、動かして確かめます。

切り捨てと round() の違いコードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます

299000円は、切り捨てなら29万円、round() なら30万円です。25000 は2のままなのに、35000 は3から4に変わります。round() は、ちょうど半分のとき偶数に寄せる決まり(Lesson 04 の「銀行丸め」)があるためです。どちらも、もっともらしく見えるのが、やっかいなところです。

整理するとは、書き方を変えること。ただし、同じ入力からは、同じ結果が出なければならない。これが、リファクタリングの約束です。

STEP 4

前と後を、同じ入力で比べる

目で比べるだけでは危険です。そこで、整理する前のコードの出力をそのまま控えておき、整理した後の出力と、プログラムに比べさせます。これは、直したせいで以前の動きが壊れる後戻り(リグレッション)を防ぐ確認で、Lesson 17 で学んだ assert による確認と同じ発想です。

次のコードは、AIの整理した版(版1)と、「親切のつもりで丸めを round() に変えられてしまった版」(版2)を、控えと比べます。

控えた出力と、2つの版を比べるサンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

版2は、3、4、5行目が違うと、すぐに分かりました。check が使っている zip と enumerate は、Lesson 05 で学んだものです。一致しないとき、プログラムを止めたいなら、assert を使います。

assert:一致しないと、止まるコードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます

AssertionError は、「確かめたことが、成り立たなかった」というお知らせです。最後の print までは進みません。

もう1つ、気をつけたいのが try / except です。足されても、正しいデータでは結果が変わらないので、控えとの比べでは見えません。壊れた行(数量が空欄)を入れて、整理する前のコードを動かしてみます。

整理する前のコードに、壊れた行を入れるコードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます

ValueError で止まりました。止まるのは不便ですが、間違ったデータがあることに気づけます。try / except で黙って飛ばす版は、同じデータでも「合計 0」と表示するだけで、何も教えてくれません。お願いに「エラー処理は足さない」と書いたのは、このためです。

「名前を直して、関数に分けて、エラー処理もログも足して」と1度に頼むと、どこが変わったのか追えません。1回に、1つの目的で頼み、そのたびに前後を比べます。新しい機能は、整理が終わってから、別の依頼で足しましょう。

STEP 5

自分で書く

お題① 整理する前と後の出力を比べて、違う行の番号(1から数える)をリストにして表示してください。

お題①:違う行の番号を調べるコードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

[no for no, (b, a) in enumerate(zip(before, after), start=1) if b != a] の形です。zip で前と後を組み、enumerate で番号を付けます。

答えの例を見る
before = ["C001 240000 24", "C002 1500 0", "C003 299000 29", "C004 87000 8"]
after = ["C001 240000 24", "C002 1500 0", "C003 299000 30", "C004 87000 9"]

diff = [no for no, (b, a) in enumerate(zip(before, after), start=1) if b != a]
print(diff)

お題② 次は、AIが書き直した表示部分です。控え(BEFORE)と一致しません。2か所を直して、「一致」と表示されるようにしてください。

お題②:AIの変更を、元の動きに戻すコードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

1つは並び順です。BEFORE は得意先コードの順なので、金額の大きい順に並べている部分(key=... と reverse=True)をやめます。もう1つは、round(amount / 10000) を、切り捨ての amount // 10000 に戻します。

答えの例を見る
totals = {"C003": 299000, "C001": 240000, "C004": 87000}
BEFORE = ["C001 240000 24", "C003 299000 29", "C004 87000 8"]

lines = []
for code, amount in sorted(totals.items()):
  lines.append(f"{code} {amount} {amount // 10000}")

print("一致" if lines == BEFORE else "不一致")
STEP 6

仕事ならどう使う?

理解チェックAIに整理を頼むとき、いちばん大切な一文は、どれでしょう?

理解チェック整理後のコードを、控えと比べたら、3行が違っていました。どうしますか?