4日目のゴール
4日目のテーマは
「JSON保存メモアプリのコードを“整理されたアプリの形”にすること」 です。
1〜3日目で、あなたはすでに
JSONでメモを保存できる
起動時にメモを復元できる
メモの追加・編集・削除・検索ができる
という「機能」は手に入れました。
4日目は、その機能を
読みやすく・直しやすく・壊れにくい構造に組み立て直す日 です。
ここからが「中級編らしさ」が本格的に出てきます。
コードがごちゃつく典型パターンを知る
なんでもかんでも main に書いてしまう問題
3日目までの流れで書いていくと、
だいたいこんな状態になりがちです。
メニュー表示も
ユーザー入力も
メモの追加・編集・削除も
JSONの読み書きも
全部が main() の近くにベタベタ書かれていく。
動きはします。
でも、こうなると
どこを直せばいいか分かりづらい
同じようなコードが何度も出てくる
機能を増やすたびに main が肥大化する
という「中級者の壁」にぶつかります。
4日目では、ここから一歩進んで
「役割ごとにコードを分ける」 という考え方を身につけます。
アプリを3つの役割に分解して考える
データ管理・データ操作・アプリの流れ
メモJSONアプリを、こう分けて考えます。
データ管理
JSONファイルから読み込む
JSONファイルに保存する
データ操作(ロジック)
メモを追加する
メモを編集する
メモを削除する
メモを検索する
アプリの流れ(UI)
メニューを表示する
ユーザーから番号や文字を入力してもらう
どの処理を呼ぶかを決める
この3つを意識して分けるだけで、
コードの見通しが一気によくなります。
データ管理担当:JSON読み書きを1か所にまとめる
load と save を「アプリの入り口と出口」にする
まずは、JSONの読み書きだけを担当する関数を
きれいに切り出します。
import json
import os
FILENAME = "memos.json"
def load_memos():
if not os.path.exists(FILENAME):
return []
with open(FILENAME, "r", encoding="utf-8") as f:
return json.load(f)
def save_memos(memos):
with open(FILENAME, "w", encoding="utf-8") as f:
json.dump(memos, f, ensure_ascii=False, indent=2)
Pythonここで深掘りしたいポイントは三つあります。
一つ目は、ファイル名を FILENAME として一箇所にまとめていることです。
もしファイル名を変えたくなっても、ここだけ直せば済みます。
二つ目は、load_memos の中で「ファイルがなければ空リストを返す」としていることです。
これにより、初回起動時でもエラーにならず、
「メモがまだない状態」として自然に動き始めます。
三つ目は、他のコードは JSON の存在を意識しなくてよくなることです。
「メモを読みたいときは load_memos() を呼ぶ」
「保存したいときは save_memos(memos) を呼ぶ」
というインターフェースだけ覚えておけばよくなります。
データ操作担当:メモをどう扱うかをまとめる
「メモ1件」と「メモ一覧」の形をはっきりさせる
メモ1件は、こういう辞書でした。
memo = {
"text": "牛乳を買う"
}
Python複数のメモはリストで持ちます。
memos = [
{"text": "牛乳を買う"},
{"text": "Pythonの勉強を30分する"}
]
Pythonこの memos に対して行う操作を
専用の関数としてまとめていきます。
メモ追加を「ロジック」として切り出す
ここで大事なのは、
「ユーザー入力」と「データ操作」を分けることです。
まずは「メモを追加するロジック」だけを書くと、こうなります。
def add_memo(memos, text):
if text == "":
return False
memo = {"text": text}
memos.append(memo)
return True
Pythonこの関数は、画面表示を一切しません。
「空文字なら追加しない」「成功したら True を返す」
という“ルール”だけを担当しています。
ユーザーからの入力は、別の場所でやります。
def input_and_add_memo(memos):
text = input("メモを入力してください: ")
if add_memo(memos, text):
print("メモを追加しました。")
else:
print("空のメモは追加しません。")
Pythonこの分け方のメリットは、
テストしやすい(add_memo に直接文字列を渡して試せる)
後でUIを変えてもロジックを変えなくていい
というところです。
メモ編集・削除も「ロジック」と「UI」を分ける
編集も同じ考え方で分けられます。
まずは「番号で指定されたメモを新しいテキストに変える」ロジック。
def edit_memo_by_index(memos, index, new_text):
if index < 0 or index >= len(memos):
return False
if new_text == "":
return False
memos[index]["text"] = new_text
return True
Pythonそして、「ユーザーに番号と新しい内容を聞く」部分。
def input_and_edit_memo(memos):
if not memos:
print("編集できるメモがありません。")
return
show_memos(memos)
num_text = input("編集するメモの番号を入力してください: ")
if not num_text.isdigit():
print("数字を入力してください。")
return
num = int(num_text)
index = num - 1
if index < 0 or index >= len(memos):
print("その番号のメモはありません。")
return
old_text = memos[index]["text"]
print(f"現在の内容: {old_text}")
new_text = input("新しい内容を入力してください(空なら変更しません): ")
if edit_memo_by_index(memos, index, new_text):
print("メモを更新しました。")
else:
print("変更をキャンセルしました。")
Python削除も同じです。
def delete_memo_by_index(memos, index):
if index < 0 or index >= len(memos):
return None
return memos.pop(index)
Pythondef input_and_delete_memo(memos):
if not memos:
print("削除できるメモがありません。")
return
show_memos(memos)
num_text = input("削除するメモの番号を入力してください: ")
if not num_text.isdigit():
print("数字を入力してください。")
return
num = int(num_text)
index = num - 1
deleted = delete_memo_by_index(memos, index)
if deleted is None:
print("その番号のメモはありません。")
else:
print(f"削除しました: {deleted['text']}")
Pythonここでの重要ポイントは、
edit_memo_by_index や delete_memo_by_index は
「インデックスが正しいか」「空文字かどうか」などの
ロジックだけを担当していることです。
一方で、input_and_edit_memo や input_and_delete_memo は
「ユーザーに何を聞くか」「どうメッセージを出すか」だけを担当しています。
表示担当:一覧表示を「何度でも使える部品」にする
show_memos を“汎用表示関数”として育てる
表示関数は、できるだけシンプルに保ちます。
def show_memos(memos):
if not memos:
print("メモはまだありません。")
return
print("=== メモ一覧 ===")
for i, memo in enumerate(memos, start=1):
print(f"{i}. {memo['text']}")
Pythonこの関数は、
全メモを表示するとき
編集前に一覧を見せるとき
削除前に一覧を見せるとき
どの場面でも同じように使えます。
「渡された memos をそのまま表示するだけ」
という役割に徹しているからこそ、
いろんな場面で再利用できるのです。
アプリの流れ担当:main を“司令塔”にする
main は「何をするか」だけを決める
ここまでで、
JSON読み書き(load / save)
メモ操作(add / edit / delete / search)
表示(show)
が、それぞれ関数として用意できました。
最後に、アプリ全体の流れを管理する main を書きます。
def main():
memos = load_memos()
print("JSONファイルからメモを読み込みました。")
while True:
print("\n=== メニュー ===")
print("1: メモを追加")
print("2: メモ一覧を表示")
print("3: メモを編集")
print("4: メモを削除")
print("5: メモを検索")
print("0: 終了")
choice = input("番号を選んでください: ")
if choice == "1":
input_and_add_memo(memos)
save_memos(memos)
elif choice == "2":
show_memos(memos)
elif choice == "3":
input_and_edit_memo(memos)
save_memos(memos)
elif choice == "4":
input_and_delete_memo(memos)
save_memos(memos)
elif choice == "5":
search_memos(memos)
elif choice == "0":
save_memos(memos)
print("終了します。")
break
else:
print("正しい番号を入力してください。")
Python最後に、エントリーポイントを書きます。
if __name__ == "__main__":
main()
Pythonここで注目してほしいのは、main が「何をするか」だけを書いていて、
「どうやるか」は他の関数に任せていることです。
4日目の完成コードを通して眺める
「役割ごとにまとまっている」感覚をつかむ
4日目時点の全体像は、こんな感じになります。
import json
import os
FILENAME = "memos.json"
def load_memos():
if not os.path.exists(FILENAME):
return []
with open(FILENAME, "r", encoding="utf-8") as f:
return json.load(f)
def save_memos(memos):
with open(FILENAME, "w", encoding="utf-8") as f:
json.dump(memos, f, ensure_ascii=False, indent=2)
def add_memo(memos, text):
if text == "":
return False
memo = {"text": text}
memos.append(memo)
return True
def input_and_add_memo(memos):
text = input("メモを入力してください: ")
if add_memo(memos, text):
print("メモを追加しました。")
else:
print("空のメモは追加しません。")
def edit_memo_by_index(memos, index, new_text):
if index < 0 or index >= len(memos):
return False
if new_text == "":
return False
memos[index]["text"] = new_text
return True
def input_and_edit_memo(memos):
if not memos:
print("編集できるメモがありません。")
return
show_memos(memos)
num_text = input("編集するメモの番号を入力してください: ")
if not num_text.isdigit():
print("数字を入力してください。")
return
num = int(num_text)
index = num - 1
if index < 0 or index >= len(memos):
print("その番号のメモはありません。")
return
old_text = memos[index]["text"]
print(f"現在の内容: {old_text}")
new_text = input("新しい内容を入力してください(空なら変更しません): ")
if edit_memo_by_index(memos, index, new_text):
print("メモを更新しました。")
else:
print("変更をキャンセルしました。")
def delete_memo_by_index(memos, index):
if index < 0 or index >= len(memos):
return None
return memos.pop(index)
def input_and_delete_memo(memos):
if not memos:
print("削除できるメモがありません。")
return
show_memos(memos)
num_text = input("削除するメモの番号を入力してください: ")
if not num_text.isdigit():
print("数字を入力してください。")
return
num = int(num_text)
index = num - 1
deleted = delete_memo_by_index(memos, index)
if deleted is None:
print("その番号のメモはありません。")
else:
print(f"削除しました: {deleted['text']}")
def search_memos(memos):
keyword = input("検索キーワードを入力してください: ")
if keyword == "":
print("キーワードが空です。")
return
results = []
for memo in memos:
if keyword in memo["text"]:
results.append(memo)
if not results:
print("そのキーワードを含むメモはありません。")
return
print("=== 検索結果 ===")
for i, memo in enumerate(results, start=1):
print(f"{i}. {memo['text']}")
def show_memos(memos):
if not memos:
print("メモはまだありません。")
return
print("=== メモ一覧 ===")
for i, memo in enumerate(memos, start=1):
print(f"{i}. {memo['text']}")
def main():
memos = load_memos()
print("JSONファイルからメモを読み込みました。")
while True:
print("\n=== メニュー ===")
print("1: メモを追加")
print("2: メモ一覧を表示")
print("3: メモを編集")
print("4: メモを削除")
print("5: メモを検索")
print("0: 終了")
choice = input("番号を選んでください: ")
if choice == "1":
input_and_add_memo(memos)
save_memos(memos)
elif choice == "2":
show_memos(memos)
elif choice == "3":
input_and_edit_memo(memos)
save_memos(memos)
elif choice == "4":
input_and_delete_memo(memos)
save_memos(memos)
elif choice == "5":
search_memos(memos)
elif choice == "0":
save_memos(memos)
print("終了します。")
break
else:
print("正しい番号を入力してください。")
if __name__ == "__main__":
main()
Pythonここまで来ると、
JSONの読み書きは load_memos / save_memos にまとまっている
メモの操作ロジックは add / edit / delete にまとまっている
アプリの流れは main にまとまっている
という「三層構造」がはっきり見えてくるはずです。
4日目で絶対に押さえてほしい本質
「機能」だけでなく「構造」を設計する
今日の一番大事なポイントは、
同じことをするコードでも
“どう分けて書くか”で、アプリの質が変わる
ということです。
JSONを扱う技術そのものは、1〜3日目でほぼ出揃っています。
4日目は、それを「アプリとして長く付き合える形」にする日です。
データ管理(JSON)
データ操作(ロジック)
アプリの流れ(UI)
この三つを意識して分ける感覚は、
この先どんな言語・どんなフレームワークを使っても役に立ちます。
ここまで来たあなたなら、
「ファイルを分けてモジュール化したい」
「クラスで書き直してみたい」
という欲もきっと出てきます。
それはもう、立派な“中級者の入り口”に立っているサインです。


