6日目のゴール
6日目のテーマは
「アプリ全体をクラスとしてまとめ、“アプリそのもの”をオブジェクトとして扱えるようになること」 です。
ここまでであなたは、
Product クラスで「商品という型」を作り
複数の商品をリストで管理し
CRUD・検索・並び替え・ファイル保存/読み込み
までを、関数とグローバル変数の組み合わせで作ってきました。
6日目では、ここから一段階進んで、
「商品管理アプリそのもの」をクラスにする
アプリの状態(商品一覧・ファイル名など)を __init__ で初期化する
メニュー処理やループもクラスのメソッドとしてまとめる
という「一段大きな粒度のクラス設計」を体験してもらいます。
まずは整理 アプリはいま何を持っているか
これまでの構成を言葉で分解する
5日目までのアプリを、ざっくり言葉で分解すると、こうなります。
商品を表す Product クラスがある。
商品一覧を表す products リストがある。
商品を追加・更新・削除・検索・表示する関数がある。
商品一覧をファイルに保存・読み込みする関数がある。
メニューを表示し、ユーザーの入力に応じて関数を呼び分ける main がある。
このうち、
「products リスト」
「データファイル名」
「メニューとループ」
は、本来「アプリ全体の状態・振る舞い」です。
これを一つのクラスにまとめると、
コードの見通しが一気によくなります。
アプリ全体を表すクラス ProductApp を作る
「アプリというオブジェクト」を作る発想
新しく ProductApp というクラスを作り、
「商品管理アプリそのもの」をオブジェクトとして表現します。
まずは骨組みから。
class ProductApp:
def __init__(self, data_file):
self.data_file = data_file
self.products = []
Pythonここで深掘りしたいポイントは二つです。
一つ目は、__init__ が「アプリが生まれた瞬間に呼ばれる初期化処理」であること。
二つ目は、self.data_file と self.products が「このアプリオブジェクトが持つ状態」になっていること。
つまり、
app = ProductApp("products.json")
とした瞬間に、
「このアプリは products.json を使う」
「このアプリは products という商品一覧を持っている」
という状態がセットされます。
ファイル読み込み・保存をアプリのメソッドにする
「アプリが自分でデータを読み書きする」形にする
5日目では、ファイル読み込み・保存は
独立した関数として書いていました。
これを ProductApp のメソッドに移します。
import json
class ProductApp:
def __init__(self, data_file):
self.data_file = data_file
self.products = []
def load_products(self):
try:
with open(self.data_file, "r", encoding="utf-8") as f:
data_list = json.load(f)
except FileNotFoundError:
print(f"{self.data_file} が見つかりません。空の商品一覧から始めます。")
self.products = []
return
except json.JSONDecodeError:
print("ファイルの内容が壊れているか、JSON ではありません。空の商品一覧から始めます。")
self.products = []
return
except OSError as e:
print("ファイルの読み込み中にエラーが発生しました。空の商品一覧から始めます。")
print("詳細:", e)
self.products = []
return
self.products = [Product.from_dict(d) for d in data_list]
print(f"商品情報をファイルから読み込みました: {self.data_file}")
def save_products(self):
data_list = [p.to_dict() for p in self.products]
try:
with open(self.data_file, "w", encoding="utf-8") as f:
json.dump(data_list, f, ensure_ascii=False, indent=2)
print(f"商品情報をファイルに保存しました: {self.data_file}")
except OSError as e:
print("ファイルの保存中にエラーが発生しました。")
print("詳細:", e)
Pythonここでの重要ポイントは、
self.data_file と self.products を使っていること。
読み込み後に self.products に Product のリストをセットしていること。
保存時には self.products から辞書のリストを作っていること。
つまり、
「このアプリオブジェクトは、自分の持つ商品一覧を、自分のファイルに読み書きできる」
という状態になっています。
商品操作の関数を「アプリのメソッド」に移す
add_product をメソッド化する
これまでの add_product(products) を、ProductApp のメソッドとして書き直します。
class ProductApp:
# __init__, load_products, save_products は省略
def add_product(self):
print("=== 商品登録 ===")
product_id = input("商品ID: ").strip()
name = input("商品名: ").strip()
price_text = input("価格(整数): ").strip()
stock_text = input("在庫数(整数): ").strip()
if not price_text.isdigit() or not stock_text.isdigit():
print("価格と在庫数は数字で入力してください。")
return
price = int(price_text)
stock = int(stock_text)
product = Product(product_id, name, price, stock)
self.products.append(product)
print("商品を登録しました。")
Pythonここでのポイントは、
products ではなく self.products を使っていること。
引数に products を渡さなくてよくなっていること。
「アプリが自分の中の products を直接操作している」
という形になっています。
一覧表示もメソッドにする
同じように、一覧表示も移します。
def show_all_products(self):
print("=== 商品一覧 ===")
if not self.products:
print("商品がまだ登録されていません。")
return
for product in self.products:
product.show_info()
print("----------")
Pythonここでも、self.products をぐるっと回しているだけです。
検索・更新・削除もアプリの中に入れる
IDで商品を探すメソッド
まずは、find_product_by_id をProductApp のメソッドにします。
def find_product_by_id(self, product_id):
for product in self.products:
if product.product_id == product_id:
return product
return None
Pythonこれで、アプリの中から
p = self.find_product_by_id("p001")
Pythonのように呼べます。
更新メソッド
更新も、self.products と self.find_product_by_id を使う形にします。
def update_product(self):
print("=== 商品更新 ===")
product_id = input("更新したい商品ID: ").strip()
product = self.find_product_by_id(product_id)
if product is None:
print("そのIDの商品は存在しません。")
return
print("現在の情報:")
product.show_info()
new_name = input("新しい商品名(変更しない場合は空欄のまま): ").strip()
new_price_text = input("新しい価格(変更しない場合は空欄のまま): ").strip()
new_stock_text = input("新しい在庫数(変更しない場合は空欄のまま): ").strip()
if new_name != "":
product.name = new_name
if new_price_text != "":
if not new_price_text.isdigit():
print("価格は数字で入力してください。価格の変更は行いません。")
else:
product.price = int(new_price_text)
if new_stock_text != "":
if not new_stock_text.isdigit():
print("在庫数は数字で入力してください。在庫数の変更は行いません。")
else:
product.stock = int(new_stock_text)
print("商品情報を更新しました。")
product.show_info()
Python削除メソッド
削除も同様です。
def delete_product(self):
print("=== 商品削除 ===")
product_id = input("削除したい商品ID: ").strip()
product = self.find_product_by_id(product_id)
if product is None:
print("そのIDの商品は存在しません。")
return
print("削除対象の商品:")
product.show_info()
confirm = input("本当に削除しますか? (y/N): ").strip().lower()
if confirm == "y":
self.products.remove(product)
print("商品を削除しました。")
else:
print("削除をキャンセルしました。")
Pythonここまで来ると、
「アプリの中に、アプリに必要な操作が全部入っている」
という感覚が出てきます。
メニューとメインループもクラスに入れる
show_menu と run をアプリのメソッドにする
最後に、メニュー表示とメインループをProductApp の中に入れます。
def show_menu(self):
print("==========")
print("商品管理アプリ(クラス編 6日目)")
print("1: 商品を登録する")
print("2: 商品一覧を表示する")
print("3: 商品情報を更新する")
print("4: 商品を削除する")
print("5: 商品をIDで表示する")
print("6: 在庫が少ない商品を表示する")
print("7: 商品一覧(価格の安い順)")
print("8: 商品一覧(名前順)")
print("9: 商品情報をファイルに保存する")
print("0: 終了")
print("==========")
def run(self):
self.load_products()
while True:
self.show_menu()
choice = input("番号を選んでください: ").strip()
if choice == "1":
self.add_product()
elif choice == "2":
self.show_all_products()
elif choice == "3":
self.update_product()
elif choice == "4":
self.delete_product()
elif choice == "5":
self.show_product_detail()
elif choice == "6":
self.show_low_stock_products()
elif choice == "7":
self.show_products_sorted_by_price()
elif choice == "8":
self.show_products_sorted_by_name()
elif choice == "9":
self.save_products()
elif choice == "0":
print("アプリを終了します。変更を保存します。")
self.save_products()
break
else:
print("不正な入力です。0〜9 のどれかを選んでください。")
Pythonここでの大事なポイントは、
run が「このアプリオブジェクトを動かすメインループ」になっていること。self.load_products() で「起動時にデータを読み込んでいる」こと。
各機能を self.メソッド名() で呼び分けていること。
これで、外側のコードは
とてもシンプルになります。
def main():
app = ProductApp("products.json")
app.run()
main()
Python「アプリを作って、アプリを走らせる」
という、直感的な形になりました。
6日目のまとめ 今日つかんでほしい感覚
今日の本質は、これです。
__init__ は「オブジェクトが生まれた瞬間に、そのオブジェクトの状態を初期化する場所」。
Product は「商品という型」、ProductApp は「商品管理アプリという型」。
アプリ全体をクラスにすると、「状態(products, data_file)と振る舞い(CRUD, 保存, ループ)」が一つにまとまる。self は「このアプリオブジェクト自身」であり、self.products や self.data_file によって状態を持てる。
外側から見ると、「app = ProductApp(…); app.run()」という、とても自然な形でアプリを扱える。
ここまで来たあなたは、
「クラスでデータを表現する」だけでなく
「クラスでアプリ全体を表現する」という一段上の設計に、
しっかり足を踏み入れています。
7日目では、このアプリ全体の構造を
自分の言葉で説明したり、少しアレンジしたりしながら、
中級編の総仕上げをしていきます。


