- Day 24:オブジェクト指向を「実践レベル」に一歩進める
- 継承:共通部分を「親クラス」にまとめる
- オーバーライド:親クラスの振る舞いを「上書き」する
- @property:メソッドを「属性っぽく」見せる
- カプセル化:内部の状態を「守る」設計
- Day 24ミニテンプレート:オブジェクト指向実践スクリプト
- Day 24のまとめ
- Day 24:オブジェクト指向実践で「商品管理システム」を作る
- 商品管理システムの全体像をイメージする
- 基底クラス Product を設計する
- 物理商品クラス PhysicalProduct を継承で作る
- デジタル商品クラス DigitalProduct を継承で作る
- 商品管理システムの「管理クラス」を作る
- 商品管理システムの全体テンプレート
- Day 24(制作編)のまとめ
Day 24:オブジェクト指向を「実践レベル」に一歩進める
Day 24では、オブジェクト指向の実践的な要素である 継承・オーバーライド・@property・カプセル化 を学びます。 「クラスをただ作る」から、「クラス同士の関係を設計する」段階に進むイメージで進めていきます。
継承:共通部分を「親クラス」にまとめる
継承とは何か
継承(inheritance) は、
- 共通する性質や振る舞いを「親クラス」にまとめて
- それを「子クラス」が引き継ぐ
という仕組みです。
イメージとしては、
- 「ユーザー」という親クラス
- 「管理者ユーザー」「一般ユーザー」という子クラス
のように、「共通部分+追加の特徴」を表現するためのものです。
基本的な継承の書き方
まずは、親クラス User を定義します。
class User:
"""ユーザーの基本的なクラスです。"""
def __init__(self, name, email):
self.name = name
self.email = email
def introduce(self):
"""自己紹介を表示します。"""
print(f"こんにちは、{self.name}です。メールアドレスは {self.email} です。")
Pythonこの User を継承した「管理者ユーザー」を作ってみます。
class AdminUser(User):
"""管理者ユーザーを表すクラスです。Userを継承しています。"""
def __init__(self, name, email, level):
# 親クラス(User)の初期化を呼び出します。
super().__init__(name, email)
# AdminUser独自の属性を追加します。
self.level = level
def show_admin_info(self):
"""管理者としての情報を表示します。"""
print(f"管理者レベル: {self.level}")
Pythonここでの重要ポイントは、
class AdminUser(User):と書くことで、「Userを継承したAdminUser」を定義していることsuper().__init__(name, email)で「親クラスの初期化処理」を呼び出していること- 子クラスに「独自の属性」や「独自のメソッド」を追加できること
です。
継承したクラスを使ってみる
admin = AdminUser("Alice", "alice@example.com", level=10)
admin.introduce() # 親クラスのメソッドが使えます
admin.show_admin_info() # 子クラス独自のメソッドも使えます
Python継承によって、
- 共通部分(名前・メールアドレス・自己紹介)は親クラスにまとめ
- 管理者としての追加情報(レベル)や振る舞いは子クラスに追加する
という構造が自然に表現できます。
オーバーライド:親クラスの振る舞いを「上書き」する
オーバーライドとは何か
オーバーライド(override) は、
- 親クラスに定義されているメソッドを
- 子クラスで「書き換える(上書きする)」
ことです。
例えば、「管理者ユーザーは自己紹介の仕方を少し変えたい」という場合です。
メソッドをオーバーライドする
先ほどの User クラスを少し使い回して、AdminUser で introduce() をオーバーライドしてみます。
class User:
"""ユーザーの基本的なクラスです。"""
def __init__(self, name, email):
self.name = name
self.email = email
def introduce(self):
"""自己紹介を表示します。"""
print(f"こんにちは、{self.name}です。メールアドレスは {self.email} です。")
class AdminUser(User):
"""管理者ユーザーを表すクラスです。"""
def __init__(self, name, email, level):
super().__init__(name, email)
self.level = level
def introduce(self):
"""管理者としての自己紹介を表示します(オーバーライド)。"""
print(f"【管理者】{self.name}(レベル: {self.level})です。連絡先は {self.email} です。")
Python使ってみます。
user = User("Bob", "bob@example.com")
admin = AdminUser("Alice", "alice@example.com", level=10)
user.introduce()
admin.introduce()
Python出力イメージ:
こんにちは、Bobです。メールアドレスは bob@example.com です。
【管理者】Alice(レベル: 10)です。連絡先は alice@example.com です。
ここでの重要ポイントは、
- 同じメソッド名
introduceを子クラスで定義し直すことで、「振る舞いを変えている」こと - 呼び出し側は
admin.introduce()と書くだけで、「管理者用の自己紹介」が実行されること
です。これにより、「共通のインターフェースを保ちつつ、振る舞いだけ変える」ことができます。
@property:メソッドを「属性っぽく」見せる
なぜ@propertyが必要なのか
オブジェクト指向では、「内部の計算結果」を外から「属性のように」扱いたい場面があります。
例えば、「フルネーム」を考えてみます。
class Person:
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
Pythonこの人の「フルネーム」は、first_name + " " + last_name で作れますが、 毎回 person.first_name + " " + person.last_name と書くのは少し面倒です。
そこで、@property を使って「フルネーム」を属性のように扱えるようにします。
@propertyの基本的な使い方
class Person:
"""人を表すクラスです。"""
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
@property
def full_name(self):
"""フルネームを返すプロパティです。"""
return f"{self.first_name} {self.last_name}"
Python使ってみます。
person = Person("Alice", "Smith")
print(person.full_name) # メソッド呼び出しではなく「属性アクセス」の形で使えます
Pythonここでの重要ポイントは、
@propertyを付けたメソッドは、「属性のように」アクセスできることperson.full_nameと書いているのに、内部ではメソッドが呼ばれていること- 「計算が必要な値」を、自然な形で扱えるようになること
です。
setterを使って「書き込み」も制御する
@property は「読み取り専用」にもできますが、 @full_name.setter を使うと「書き込み時の処理」も定義できます。
class Person:
"""フルネームをプロパティで扱う例です。"""
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
@property
def full_name(self):
"""フルネームを返します。"""
return f"{self.first_name} {self.last_name}"
@full_name.setter
def full_name(self, value):
"""フルネームを 'First Last' 形式で受け取り、first_name と last_name に分割します。"""
parts = value.split(" ", 1)
if len(parts) != 2:
raise ValueError("フルネームは 'First Last' 形式で指定してください。")
self.first_name, self.last_name = parts
Python使ってみます。
person = Person("Alice", "Smith")
print(person.full_name) # Alice Smith
person.full_name = "Bob Johnson" # setterが呼ばれます
print(person.first_name) # Bob
print(person.last_name) # Johnson
Pythonここでのポイントは、
full_nameという「1つのプロパティ」を通して、内部の2つの属性をまとめて扱っていること- setterの中で「入力値の形式チェック」を行えること
です。これが、次に説明する「カプセル化」ともつながっていきます。
カプセル化:内部の状態を「守る」設計
カプセル化とは何か
カプセル化(encapsulation) は、
- オブジェクトの内部状態(属性)を
- 外部から直接いじられないように守り
- 必要な操作だけをメソッドやプロパティとして公開する
という考え方です。
Pythonでは「完全な隠蔽」はありませんが、 慣習として「外から直接触ってほしくない属性」を _ や __ で始めることで表現します。
「直接いじってほしくない属性」を隠す
例えば、「銀行口座」を表現するクラスを考えます。
class BankAccount:
"""銀行口座を表すクラスです。"""
def __init__(self, owner, balance=0):
self.owner = owner
self._balance = balance # 外から直接いじってほしくないので、先頭に _ を付けています。
@property
def balance(self):
"""残高を読み取るためのプロパティです。"""
return self._balance
def deposit(self, amount):
"""入金処理を行います。"""
if amount <= 0:
raise ValueError("入金額は正の数である必要があります。")
self._balance += amount
def withdraw(self, amount):
"""出金処理を行います。"""
if amount <= 0:
raise ValueError("出金額は正の数である必要があります。")
if amount > self._balance:
raise ValueError("残高不足です。")
self._balance -= amount
Python使ってみます。
account = BankAccount("Alice", balance=1000)
print("残高:", account.balance) # プロパティ経由で残高を参照します。
account.deposit(500)
print("入金後の残高:", account.balance)
account.withdraw(300)
print("出金後の残高:", account.balance)
Pythonここでの重要ポイントは、
_balanceを「外から直接いじってほしくない内部属性」として扱っていること- 残高の変更は
depositとwithdrawを通してのみ行うようにしていること - その中で「不正な操作(マイナス入金・残高不足の出金)」を防いでいること
です。これがカプセル化の本質であり、「オブジェクトの中にルールを閉じ込める」イメージです。
Day 24ミニテンプレート:オブジェクト指向実践スクリプト
最後に、Day 24で学んだ内容をまとめて試せる「オブジェクト指向実践スクリプト」のテンプレートを示します。
# day24_oop_practice.py
# オブジェクト指向実践の練習用スクリプト
class User:
"""ユーザーの基本クラスです。"""
def __init__(self, name, email):
self.name = name
self.email = email
def introduce(self):
"""自己紹介を表示します。"""
print(f"こんにちは、{self.name}です。メールアドレスは {self.email} です。")
class AdminUser(User):
"""管理者ユーザークラスです。Userを継承しています。"""
def __init__(self, name, email, level):
super().__init__(name, email)
self.level = level
def introduce(self):
"""管理者としての自己紹介(オーバーライド)。"""
print(f"【管理者】{self.name}(レベル: {self.level})です。連絡先は {self.email} です。")
class Person:
"""@property の例としての人クラスです。"""
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
@property
def full_name(self):
"""フルネームを返すプロパティです。"""
return f"{self.first_name} {self.last_name}"
@full_name.setter
def full_name(self, value):
"""フルネームを 'First Last' 形式で受け取り、分割します。"""
parts = value.split(" ", 1)
if len(parts) != 2:
raise ValueError("フルネームは 'First Last' 形式で指定してください。")
self.first_name, self.last_name = parts
class BankAccount:
"""カプセル化の例としての銀行口座クラスです。"""
def __init__(self, owner, balance=0):
self.owner = owner
self._balance = balance # 内部属性(直接いじってほしくない)
@property
def balance(self):
"""残高を読み取るためのプロパティです。"""
return self._balance
def deposit(self, amount):
"""入金処理を行います。"""
if amount <= 0:
raise ValueError("入金額は正の数である必要があります。")
self._balance += amount
def withdraw(self, amount):
"""出金処理を行います。"""
if amount <= 0:
raise ValueError("出金額は正の数である必要があります。")
if amount > self._balance:
raise ValueError("残高不足です。")
self._balance -= amount
def main():
print("=== 継承とオーバーライドの例 ===")
user = User("Bob", "bob@example.com")
admin = AdminUser("Alice", "alice@example.com", level=10)
user.introduce()
admin.introduce()
print("\n=== @property の例 ===")
person = Person("Charlie", "Brown")
print("フルネーム:", person.full_name)
person.full_name = "Dave Smith"
print("変更後のフルネーム:", person.full_name)
print("\n=== カプセル化の例 ===")
account = BankAccount("Eve", balance=1000)
print("初期残高:", account.balance)
account.deposit(500)
print("入金後の残高:", account.balance)
account.withdraw(300)
print("出金後の残高:", account.balance)
if __name__ == "__main__":
main()
PythonDay 24のまとめ
Day 24では、オブジェクト指向実践として、
- 継承で「共通部分を親クラスにまとめ、子クラスに特徴を追加する」設計
- オーバーライドで「同じメソッド名のまま、振る舞いだけを変える」テクニック
@propertyで「計算が必要な値を属性のように扱う」書き方- カプセル化で「内部状態を守り、ルールをオブジェクトの中に閉じ込める」考え方
をステップバイステップで体験していただきました。
ここまで来ると、「クラス単体」ではなく、「クラス同士の関係」や「クラスの中に閉じ込めるルール」を意識して設計する感覚が少し育っているはずです。 この感覚は、今後の実践Python編で、より複雑なアプリケーションやドメインを扱うときに、コードを整理し続けるための強力な武器になっていきます。
Day 24:オブジェクト指向実践で「商品管理システム」を作る
Day 24の制作編では、 継承・オーバーライド・@property・カプセル化 をすべて盛り込んだ「商品管理システム」を作っていきます。 単なるクラスの練習ではなく、「現実のドメイン(商品管理)」をコードでどう表現するかを体験していただくことを目標にします。
商品管理システムの全体像をイメージする
どんなクラスを作るか
今回の商品管理システムでは、次のような構成を目指します。
- 基底クラス
Product- すべての商品に共通する「名前」「価格」「在庫数」を持つ
- 在庫の増減メソッドを持つ
- 派生クラス
PhysicalProduct(物理商品)- 配送が必要な商品(本・PC・家具など)
- 送料や在庫の扱いを少し変える
- 派生クラス
DigitalProduct(デジタル商品)- ダウンロード商品(電子書籍・ソフトウェアなど)
- 在庫の概念を変える(在庫無限など)
@propertyを使った「税込価格」や「在庫価値」の計算- カプセル化された在庫管理(不正な在庫操作を防ぐ)
これらを組み合わせて、「商品を登録し、一覧表示し、在庫を操作する」簡易システムを作ります。
基底クラス Product を設計する
共通する属性とメソッドをまとめる
まずは、すべての商品に共通する部分を Product クラスにまとめます。
class Product:
"""商品を表す基底クラスです。"""
def __init__(self, name: str, price: int, stock: int = 0):
# 商品名
self.name = name
# 価格(税抜き)
self._price = price # 外から直接いじってほしくないので、先頭に _ を付けています。
# 在庫数
self._stock = stock # これもカプセル化の対象です。
@property
def price(self) -> int:
"""税抜き価格を返すプロパティです。"""
return self._price
@price.setter
def price(self, value: int):
"""価格を設定する際のチェックを行うsetterです。"""
if value < 0:
raise ValueError("価格は0以上である必要があります。")
self._price = value
@property
def stock(self) -> int:
"""在庫数を返すプロパティです。"""
return self._stock
def add_stock(self, amount: int):
"""在庫を増やします。"""
if amount <= 0:
raise ValueError("追加する在庫数は正の数である必要があります。")
self._stock += amount
def remove_stock(self, amount: int):
"""在庫を減らします。"""
if amount <= 0:
raise ValueError("減らす在庫数は正の数である必要があります。")
if amount > self._stock:
raise ValueError("在庫不足です。")
self._stock -= amount
@property
def total_value(self) -> int:
"""在庫の総額(税抜き)を返すプロパティです。"""
return self.price * self.stock
def show_info(self):
"""商品情報を表示するメソッドです。"""
print(f"商品名: {self.name}")
print(f"価格(税抜): {self.price} 円")
print(f"在庫数: {self.stock} 個")
print(f"在庫総額(税抜): {self.total_value} 円")
Pythonここでの重要ポイントは次の通りです。
- カプセル化
_priceと_stockを「内部属性」として扱い、price・stockプロパティ経由でアクセスするようにしています。- setterで「価格は0以上」「在庫操作は正の数のみ」「在庫不足を防ぐ」といったルールを閉じ込めています。
@propertyprice・stock・total_valueをプロパティにすることで、 「計算が必要な値」も自然な形で扱えるようにしています。
- 共通メソッド
add_stock・remove_stock・show_infoは、すべての商品に共通する振る舞いです。
この Product が、物理商品・デジタル商品の「親クラス」になります。
物理商品クラス PhysicalProduct を継承で作る
送料や税込価格を追加する
物理商品は、「送料」や「税込価格」を意識することが多いです。 そこで、Product を継承した PhysicalProduct クラスを作ります。
class PhysicalProduct(Product):
"""物理商品を表すクラスです。Productを継承しています。"""
def __init__(self, name: str, price: int, stock: int = 0, shipping_fee: int = 0, tax_rate: float = 0.1):
# 親クラス(Product)の初期化を呼び出します。
super().__init__(name, price, stock)
# 物理商品独自の属性を追加します。
self.shipping_fee = shipping_fee # 送料(1回の注文あたり)
self.tax_rate = tax_rate # 消費税率(例: 0.1 = 10%)
@property
def price_with_tax(self) -> int:
"""税込価格を返すプロパティです。"""
return int(self.price * (1 + self.tax_rate))
def show_info(self):
"""商品情報を表示するメソッド(オーバーライド)。"""
print("=== 物理商品 ===")
print(f"商品名: {self.name}")
print(f"価格(税抜): {self.price} 円")
print(f"価格(税込): {self.price_with_tax} 円")
print(f"送料: {self.shipping_fee} 円")
print(f"在庫数: {self.stock} 個")
print(f"在庫総額(税抜): {self.total_value} 円")
Pythonここでの重要ポイントは次の通りです。
- 継承
class PhysicalProduct(Product):と書くことで、Productのすべての機能を引き継いでいます。
- オーバーライド
show_infoを子クラスで書き直すことで、「物理商品用の表示」に変えています。
@propertyprice_with_taxをプロパティにすることで、「税込価格」を自然な形で扱えるようにしています。
デジタル商品クラス DigitalProduct を継承で作る
在庫の概念を変える(在庫無限など)
デジタル商品(電子書籍・ソフトウェアなど)は、 「在庫切れ」という概念がないことが多いです。 そこで、Product を継承しつつ、在庫の扱いをオーバーライドしてみます。
class DigitalProduct(Product):
"""デジタル商品を表すクラスです。Productを継承しています。"""
def __init__(self, name: str, price: int):
# デジタル商品は在庫無限とみなすので、stockは0固定で親を初期化します。
super().__init__(name, price, stock=0)
@property
def stock(self) -> int:
"""在庫数を返すプロパティです。デジタル商品は在庫無限とみなします。"""
# 実際には「∞」のような概念ですが、ここでは説明用に -1 を「無限」の意味として返します。
return -1
@property
def total_value(self) -> int:
"""在庫総額は意味がないので、0を返します。"""
return 0
def add_stock(self, amount: int):
"""デジタル商品の在庫は増やす必要がないので、何もしません。"""
print("デジタル商品の在庫は管理していません。")
def remove_stock(self, amount: int):
"""デジタル商品の在庫は減らす必要がないので、何もしません。"""
print("デジタル商品の在庫は管理していません。")
def show_info(self):
"""商品情報を表示するメソッド(オーバーライド)。"""
print("=== デジタル商品 ===")
print(f"商品名: {self.name}")
print(f"価格(税抜): {self.price} 円")
print("在庫: 無制限")
Pythonここでの重要ポイントは次の通りです。
- オーバーライドで「振る舞いを変える」
stockプロパティ・total_value・add_stock・remove_stockをオーバーライドすることで、 「デジタル商品は在庫管理しない」というルールをクラスの中に閉じ込めています。
- カプセル化されたルール
- 外から見たときには「在庫無限」「在庫操作は意味がない」ということが、 クラスのメソッドを通して自然に伝わるようになっています。
商品管理システムの「管理クラス」を作る
商品一覧を管理する ProductManager
複数の商品をまとめて管理するために、ProductManager クラスを作ります。
class ProductManager:
"""商品をまとめて管理するクラスです。"""
def __init__(self):
self._products = [] # 商品インスタンスのリスト
def add_product(self, product: Product):
"""商品を追加します。"""
self._products.append(product)
def list_products(self):
"""登録されている商品を一覧表示します。"""
print("=== 商品一覧 ===")
if not self._products:
print("商品が登録されていません。")
return
for idx, product in enumerate(self._products, start=1):
print(f"\n[{idx}]")
product.show_info()
def find_by_name(self, name: str) -> Product | None:
"""商品名で検索し、見つかった商品を返します。見つからなければNoneを返します。"""
for product in self._products:
if product.name == name:
return product
return None
Pythonここでのポイントは、
Productを継承したPhysicalProduct・DigitalProductのインスタンスを、 「同じリスト_productsにまとめて管理している」ことproduct.show_info()を呼ぶだけで、 物理商品・デジタル商品それぞれの表示が「オーバーライドされたメソッド」によって自動的に切り替わること
です。これが「ポリモーフィズム(多態性)」の入り口でもあります。
商品管理システムの全体テンプレート
ここまでのクラスを組み合わせて、 「商品を登録し、一覧表示し、在庫を操作する」簡易システムの全体コードをまとめます。
# day24_product_system.py
# オブジェクト指向実践:商品管理システム
class Product:
"""商品を表す基底クラスです。"""
def __init__(self, name: str, price: int, stock: int = 0):
self.name = name
self._price = price
self._stock = stock
@property
def price(self) -> int:
"""税抜き価格を返すプロパティです。"""
return self._price
@price.setter
def price(self, value: int):
"""価格を設定する際のチェックを行うsetterです。"""
if value < 0:
raise ValueError("価格は0以上である必要があります。")
self._price = value
@property
def stock(self) -> int:
"""在庫数を返すプロパティです。"""
return self._stock
def add_stock(self, amount: int):
"""在庫を増やします。"""
if amount <= 0:
raise ValueError("追加する在庫数は正の数である必要があります。")
self._stock += amount
def remove_stock(self, amount: int):
"""在庫を減らします。"""
if amount <= 0:
raise ValueError("減らす在庫数は正の数である必要があります。")
if amount > self._stock:
raise ValueError("在庫不足です。")
self._stock -= amount
@property
def total_value(self) -> int:
"""在庫の総額(税抜き)を返すプロパティです。"""
return self.price * self.stock
def show_info(self):
"""商品情報を表示するメソッドです。"""
print("=== 商品(基底クラス) ===")
print(f"商品名: {self.name}")
print(f"価格(税抜): {self.price} 円")
print(f"在庫数: {self.stock} 個")
print(f"在庫総額(税抜): {self.total_value} 円")
class PhysicalProduct(Product):
"""物理商品を表すクラスです。Productを継承しています。"""
def __init__(self, name: str, price: int, stock: int = 0, shipping_fee: int = 0, tax_rate: float = 0.1):
super().__init__(name, price, stock)
self.shipping_fee = shipping_fee
self.tax_rate = tax_rate
@property
def price_with_tax(self) -> int:
"""税込価格を返すプロパティです。"""
return int(self.price * (1 + self.tax_rate))
def show_info(self):
"""商品情報を表示するメソッド(オーバーライド)。"""
print("=== 物理商品 ===")
print(f"商品名: {self.name}")
print(f"価格(税抜): {self.price} 円")
print(f"価格(税込): {self.price_with_tax} 円")
print(f"送料: {self.shipping_fee} 円")
print(f"在庫数: {self.stock} 個")
print(f"在庫総額(税抜): {self.total_value} 円")
class DigitalProduct(Product):
"""デジタル商品を表すクラスです。Productを継承しています。"""
def __init__(self, name: str, price: int):
super().__init__(name, price, stock=0)
@property
def stock(self) -> int:
"""在庫数を返すプロパティです。デジタル商品は在庫無限とみなします。"""
return -1 # 説明用に「-1 = 無限」としています。
@property
def total_value(self) -> int:
"""在庫総額は意味がないので、0を返します。"""
return 0
def add_stock(self, amount: int):
"""デジタル商品の在庫は増やす必要がないので、何もしません。"""
print("デジタル商品の在庫は管理していません。")
def remove_stock(self, amount: int):
"""デジタル商品の在庫は減らす必要がないので、何もしません。"""
print("デジタル商品の在庫は管理していません。")
def show_info(self):
"""商品情報を表示するメソッド(オーバーライド)。"""
print("=== デジタル商品 ===")
print(f"商品名: {self.name}")
print(f"価格(税抜): {self.price} 円")
print("在庫: 無制限")
class ProductManager:
"""商品をまとめて管理するクラスです。"""
def __init__(self):
self._products = []
def add_product(self, product: Product):
"""商品を追加します。"""
self._products.append(product)
def list_products(self):
"""登録されている商品を一覧表示します。"""
print("=== 商品一覧 ===")
if not self._products:
print("商品が登録されていません。")
return
for idx, product in enumerate(self._products, start=1):
print(f"\n[{idx}]")
product.show_info()
def find_by_name(self, name: str) -> Product | None:
"""商品名で検索し、見つかった商品を返します。見つからなければNoneを返します。"""
for product in self._products:
if product.name == name:
return product
return None
def main():
print("=== Day 24:商品管理システム ===")
manager = ProductManager()
# 物理商品を登録します。
laptop = PhysicalProduct("ノートPC", price=120000, stock=5, shipping_fee=1000, tax_rate=0.1)
book = PhysicalProduct("技術書", price=3000, stock=10, shipping_fee=500, tax_rate=0.1)
# デジタル商品を登録します。
ebook = DigitalProduct("電子書籍(PDF版)", price=2000)
software = DigitalProduct("ソフトウェアライセンス", price=15000)
manager.add_product(laptop)
manager.add_product(book)
manager.add_product(ebook)
manager.add_product(software)
# 商品一覧を表示します。
manager.list_products()
# 在庫操作の例
print("\n=== 在庫操作の例 ===")
target = manager.find_by_name("ノートPC")
if target is not None:
print("ノートPCの在庫を3個追加します。")
target.add_stock(3)
target.show_info()
target = manager.find_by_name("電子書籍(PDF版)")
if target is not None:
print("電子書籍の在庫を減らそうとします(意味がない例)。")
target.remove_stock(1)
target.show_info()
if __name__ == "__main__":
main()
PythonDay 24(制作編)のまとめ
Day 24の制作編では、「商品管理システム」を通して、
- 継承
Productを親クラスとして、PhysicalProduct・DigitalProductを派生クラスとして設計する流れ
- オーバーライド
show_infoや在庫関連メソッドを子クラスで書き換え、 「同じメソッド名で、商品種別ごとに振る舞いを変える」感覚
@propertyprice・stock・total_value・price_with_taxなどをプロパティとして扱い、 「計算が必要な値を属性のように自然に扱う」書き方
- カプセル化
_price・_stockを内部属性とし、 setterやメソッドの中に「不正な操作を防ぐルール」を閉じ込める設計
を、ひとつのまとまったプログラムの中で体験していただきました。
ここまで来ると、「オブジェクト指向の機能を、現実のドメイン(商品管理)の中でどう使うか」という感覚が少し育っているはずです。 この感覚は、今後より大きなシステムや複雑なビジネスロジックを扱うときに、コードを整理し続けるための強力な土台になっていきます。
