ステップ23:クラスの活用

🎨 ステップ23: クラスの活用

クラス変数、継承、実践的なクラス設計を学ぼう!

前のステップでクラスの基礎を学びました。このステップでは、より実践的で強力な機能を学びます!

📖 このステップで学ぶこと

・クラス変数とインスタンス変数の違い

・継承の基礎とメソッドのオーバーライド

・super()で親クラスを呼び出す

・実践的なクラス設計

🎯 1. クラス変数とインスタンス変数

🔰 2種類の変数

クラスには2種類の変数があります:

📌 クラス変数とインスタンス変数の違い

種類 定義場所 共有範囲 用途
クラス変数 クラス直下 全インスタンスで共有 カウンター、定数
インスタンス変数 __init__内(self.〜) 各インスタンスごとに独立 個別のデータ

📝 インスタンス変数(復習)

インスタンス変数は、各インスタンスごとに異なる値を持ちます。

※スマートフォンでは横スクロールできます

# インスタンス変数:各インスタンスごとに異なる値
class Dog:
    def __init__(self, name):
        self.name = name  # インスタンス変数(self.〜で定義)

# 2匹の犬はそれぞれ別の名前を持つ
pochi = Dog("ポチ")
taro = Dog("タロ")

print(pochi.name)  # ポチの名前
print(taro.name)   # タロの名前

実行結果

ポチ
タロ

📝 クラス変数

クラス変数は、すべてのインスタンスで共有されます。

※スマートフォンでは横スクロールできます

class Dog:
    # クラス変数:クラス直下に定義(すべてのインスタンスで共有)
    species = "犬"
    
    def __init__(self, name):
        self.name = name  # インスタンス変数

pochi = Dog("ポチ")
taro = Dog("タロ")

# クラス変数は全インスタンスで同じ値
print(f"{pochi.name}は{pochi.species}です")
print(f"{taro.name}は{taro.species}です")

実行結果

ポチは犬です
タロは犬です

💡 クラス変数のイメージ

クラス変数(species):「犬」という種族は全ての犬に共通

インスタンス変数(name):名前は一匹一匹違う

📝 クラス変数の実用例:インスタンスをカウント

クラス変数を使って、作成されたインスタンスの数をカウントできます。

ステップ1:クラス変数でカウンターを定義

※スマートフォンでは横スクロールできます

class Student:
    count = 0  # クラス変数:生徒の総数(初期値0)

ステップ2:__init__でカウントを増やす

※スマートフォンでは横スクロールできます

    def __init__(self, name):
        self.name = name
        # クラス名.変数名 でクラス変数にアクセス
        Student.count += 1  # インスタンスが作られるたびに+1

完成コード:生徒カウンター

※スマートフォンでは横スクロールできます

class Student:
    count = 0  # クラス変数:全インスタンスで共有
    
    def __init__(self, name):
        self.name = name
        Student.count += 1  # インスタンス作成時にカウントアップ
    
    @classmethod
    def get_count(cls):
        # クラスメソッド:クラス変数にアクセスするメソッド
        # cls はクラス自身を指す(selfと似ている)
        return cls.count

# 生徒を3人作成
student1 = Student("太郎")
student2 = Student("花子")
student3 = Student("次郎")

# クラス変数は全インスタンスで共有されている
print(f"生徒の総数: {Student.get_count()}人")

実行結果

生徒の総数: 3人

📝 @classmethodとは

@classmethod:クラスメソッドを定義するデコレータ

・第一引数はcls(クラス自身を指す)

・インスタンスを作らなくても呼び出せる

・クラス変数にアクセスする時に使う

📌 クラス変数へのアクセス方法

方法 書き方
クラス名から クラス名.変数名 Student.count
インスタンスから インスタンス.変数名 student1.count
クラスメソッド内 cls.変数名 cls.count

🔗 2. 継承 – クラスを拡張する

継承(inheritance)を使うと、既存のクラスを元に新しいクラスを作れます。共通部分を再利用できます!

🔰 継承とは

📌 継承の用語

用語 別名 説明
親クラス 基底クラス、スーパークラス 元になるクラス
子クラス 派生クラス、サブクラス 継承して作った新しいクラス

💡 継承のイメージ

〇〇は××の一種である」という関係がある時に使います:

・犬は動物の一種 → Animal クラスを継承して Dog クラスを作る

・正社員は従業員の一種 → Employee クラスを継承して FullTime クラスを作る

📝 基本的な継承

📝 継承の構文

class 子クラス名(親クラス名):

# 子クラスの内容

・括弧の中に親クラス名を書く

・子クラスは親クラスの属性とメソッドを引き継ぐ

ステップ1:親クラスを定義

※スマートフォンでは横スクロールできます

# 親クラス(基底クラス)
class Animal:
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        print(f"{self.name}が鳴いています")

ステップ2:子クラスを定義(継承)

※スマートフォンでは横スクロールできます

# 子クラス(派生クラス)
# Animal を継承して Dog を作る
class Dog(Animal):
    # 子クラス独自のメソッドを追加
    def bark(self):
        print(f"{self.name}: ワンワン!")

# Cat も Animal を継承
class Cat(Animal):
    def meow(self):
        print(f"{self.name}: ニャー!")

完成コード:基本的な継承

※スマートフォンでは横スクロールできます

# 親クラス
class Animal:
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        print(f"{self.name}が鳴いています")

# 子クラス:Animal を継承
class Dog(Animal):
    def bark(self):
        print(f"{self.name}: ワンワン!")

class Cat(Animal):
    def meow(self):
        print(f"{self.name}: ニャー!")

# インスタンス作成
dog = Dog("ポチ")
cat = Cat("タマ")

# 親クラスのメソッドが使える
dog.speak()  # Animal のメソッド
dog.bark()   # Dog 独自のメソッド

cat.speak()  # Animal のメソッド
cat.meow()   # Cat 独自のメソッド

実行結果

ポチが鳴いています
ポチ: ワンワン!
タマが鳴いています
タマ: ニャー!

💡 継承のポイント

・子クラスは親クラスの属性(name)を引き継ぐ

・子クラスは親クラスのメソッド(speak)を引き継ぐ

・子クラスには独自のメソッド(bark、meow)を追加できる

📝 メソッドのオーバーライド(上書き)

子クラスで親クラスと同じ名前のメソッドを定義すると、親のメソッドを上書きできます。

完成コード:メソッドのオーバーライド

※スマートフォンでは横スクロールできます

class Animal:
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        print(f"{self.name}が鳴いています")

class Dog(Animal):
    # 親クラスの speak を上書き(オーバーライド)
    def speak(self):
        print(f"{self.name}: ワンワン!")

class Cat(Animal):
    # 親クラスの speak を上書き(オーバーライド)
    def speak(self):
        print(f"{self.name}: ニャー!")

# 同じ speak() メソッドでも、クラスによって動作が違う
dog = Dog("ポチ")
cat = Cat("タマ")

dog.speak()  # Dog の speak が呼ばれる
cat.speak()  # Cat の speak が呼ばれる

実行結果

ポチ: ワンワン!
タマ: ニャー!

💡 オーバーライドとは

・親クラスのメソッドを子クラスで再定義すること

・同じメソッド名でも、クラスによって異なる動作にできる

・これをポリモーフィズム(多態性)と呼ぶ

🔼 3. super() – 親クラスを呼び出す

super()を使うと、子クラスから親クラスのメソッドを呼び出せます。

🔰 super()とは

📝 super()の用途

・親クラスの__init__を呼び出す

・親クラスのメソッドを呼び出してから、追加の処理をする

・親クラスの機能を拡張する時に使う

📝 super().__init__() で親クラスを初期化

子クラスで__init__を定義すると、親クラスの__init__は自動では呼ばれません。super().__init__()で明示的に呼び出します。

ステップ1:親クラスを定義

※スマートフォンでは横スクロールできます

# 親クラス
class Animal:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def introduce(self):
        print(f"名前: {self.name}, 年齢: {self.age}歳")

ステップ2:子クラスでsuper()を使う

※スマートフォンでは横スクロールできます

# 子クラス
class Dog(Animal):
    def __init__(self, name, age, breed):
        # super() で親クラスの __init__ を呼ぶ
        # これにより self.name と self.age が設定される
        super().__init__(name, age)
        
        # 子クラス独自の属性を追加
        self.breed = breed

完成コード:super().__init__()

※スマートフォンでは横スクロールできます

class Animal:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def introduce(self):
        print(f"名前: {self.name}, 年齢: {self.age}歳")

class Dog(Animal):
    def __init__(self, name, age, breed):
        # 親クラスの __init__ を呼ぶ
        super().__init__(name, age)
        # 子クラス独自の属性
        self.breed = breed
    
    def introduce(self):
        # 親クラスの introduce を呼ぶ
        super().introduce()
        # 追加の情報を表示
        print(f"犬種: {self.breed}")

dog = Dog("ポチ", 3, "柴犬")
dog.introduce()

実行結果

名前: ポチ, 年齢: 3歳
犬種: 柴犬

📌 super()の使い方まとめ

用途 書き方
親の__init__を呼ぶ super().__init__(引数)
親のメソッドを呼ぶ super().メソッド名()

📝 super()を使わないとどうなる?

super()を使わないと、親クラスの属性が初期化されません。

※スマートフォンでは横スクロールできます

class Animal:
    def __init__(self, name, age):
        self.name = name
        self.age = age

class Dog(Animal):
    def __init__(self, name, age, breed):
        # super().__init__(name, age) を書き忘れると...
        self.breed = breed

dog = Dog("ポチ", 3, "柴犬")
print(dog.breed)  # OK
print(dog.name)   # エラー! name が設定されていない

❌ エラーになる

AttributeError: 'Dog' object has no attribute 'name'

super().__init__()を呼ばないと、親クラスの属性が初期化されません。

⚠️ super()を忘れずに!

子クラスで__init__を定義したら、必ずsuper().__init__()を呼びましょう。

親クラスの初期化処理をスキップすると、属性が設定されずエラーになります。

📝 継承の実践例:ゲームキャラクター

RPGゲームのキャラクターを継承で作ってみましょう。

完成コード:ゲームキャラクター

※スマートフォンでは横スクロールできます

# 親クラス:全キャラクター共通の機能
class Character:
    def __init__(self, name, hp, attack):
        self.name = name
        self.hp = hp
        self.max_hp = hp
        self.attack = attack
    
    def take_damage(self, damage):
        self.hp -= damage
        if self.hp < 0:
            self.hp = 0
        print(f"{self.name}は{damage}のダメージ! HP: {self.hp}/{self.max_hp}")
    
    def is_alive(self):
        return self.hp > 0

# 子クラス:戦士(高HP、特殊攻撃)
class Warrior(Character):
    def __init__(self, name):
        # 親クラスの__init__を呼ぶ(HP=150, 攻撃力=25)
        super().__init__(name, hp=150, attack=25)
        self.defense = 10  # 戦士独自の属性
    
    def special_attack(self, enemy):
        # 戦士独自のメソッド:2倍ダメージ
        damage = self.attack * 2
        print(f"{self.name}の強撃!")
        enemy.take_damage(damage)

# 子クラス:魔法使い(低HP、魔法攻撃)
class Mage(Character):
    def __init__(self, name):
        super().__init__(name, hp=80, attack=30)
        self.mp = 50  # 魔法使い独自の属性
    
    def magic_attack(self, enemy):
        if self.mp >= 10:
            damage = int(self.attack * 1.5)
            self.mp -= 10
            print(f"{self.name}の魔法攻撃! MP: {self.mp}")
            enemy.take_damage(damage)
        else:
            print(f"{self.name}のMPが足りない!")

# バトル
warrior = Warrior("戦士")
mage = Mage("魔法使い")
enemy = Character("スライム", 100, 15)

warrior.special_attack(enemy)
mage.magic_attack(enemy)

実行結果

戦士の強撃!
スライムは50のダメージ! HP: 50/100
魔法使いの魔法攻撃! MP: 40
スライムは45のダメージ! HP: 5/100

💡 この例のポイント

Character:全キャラクター共通(HP、ダメージ処理)

Warrior:戦士独自(高HP、強撃、防御力)

Mage:魔法使い独自(MP、魔法攻撃)

共通部分は親クラスに、独自部分は子クラスに書くことで、コードの重複を減らせます。

🎯 4. 実践的な例

ここまで学んだクラス変数と継承を使って、実際に役立つシステムを作ってみましょう。

📝 実践例1:銀行システム

通常口座と貯蓄口座を継承で作ります。

完成コード:銀行システム

※スマートフォンでは横スクロールできます

# 親クラス:通常の銀行口座
class BankAccount:
    account_count = 0  # クラス変数:口座番号の連番
    
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance
        # クラス変数を使って口座番号を自動採番
        BankAccount.account_count += 1
        self.account_number = BankAccount.account_count
    
    def deposit(self, amount):
        self.balance += amount
        print(f"{amount}円を入金しました")
    
    def withdraw(self, amount):
        if amount > self.balance:
            print("残高不足です")
            return False
        self.balance -= amount
        print(f"{amount}円を引き出しました")
        return True
    
    def show_balance(self):
        print(f"口座番号: {self.account_number}")
        print(f"名義: {self.owner}")
        print(f"残高: {self.balance}円")

# 子クラス:貯蓄口座(利息がつく)
class SavingsAccount(BankAccount):
    def __init__(self, owner, balance=0, interest_rate=0.01):
        super().__init__(owner, balance)
        self.interest_rate = interest_rate  # 利率(独自属性)
    
    def add_interest(self):
        # 独自メソッド:利息を追加
        interest = int(self.balance * self.interest_rate)
        self.balance += interest
        print(f"利息{interest}円が追加されました")

# 使用例
print("=== 通常口座 ===")
account1 = BankAccount("太郎", 10000)
account1.deposit(5000)
account1.show_balance()

print("\n=== 貯蓄口座 ===")
savings = SavingsAccount("花子", 50000, 0.02)  # 利率2%
savings.add_interest()
savings.show_balance()

実行結果

=== 通常口座 ===
5000円を入金しました
口座番号: 1
名義: 太郎
残高: 15000円

=== 貯蓄口座 ===
利息1000円が追加されました
口座番号: 2
名義: 花子
残高: 51000円

📝 実践例2:従業員管理システム

正社員とパートタイムで給与計算を変えます。

完成コード:従業員管理

※スマートフォンでは横スクロールできます

# 親クラス:従業員
class Employee:
    def __init__(self, name, employee_id):
        self.name = name
        self.employee_id = employee_id
    
    def calculate_salary(self):
        # 子クラスでオーバーライドする
        return 0
    
    def show_info(self):
        print(f"社員ID: {self.employee_id}")
        print(f"名前: {self.name}")
        print(f"給与: {self.calculate_salary():,}円")

# 子クラス:正社員(月給制)
class FullTime(Employee):
    def __init__(self, name, employee_id, monthly_salary):
        super().__init__(name, employee_id)
        self.monthly_salary = monthly_salary
    
    def calculate_salary(self):
        return self.monthly_salary

# 子クラス:パートタイム(時給制)
class PartTime(Employee):
    def __init__(self, name, employee_id, hourly_rate, hours):
        super().__init__(name, employee_id)
        self.hourly_rate = hourly_rate
        self.hours = hours
    
    def calculate_salary(self):
        return self.hourly_rate * self.hours

# 使用例
print("=== 正社員 ===")
full = FullTime("山田太郎", "F001", 300000)
full.show_info()

print("\n=== パートタイム ===")
part = PartTime("佐藤花子", "P001", 1200, 80)
part.show_info()

実行結果

=== 正社員 ===
社員ID: F001
名前: 山田太郎
給与: 300,000円

=== パートタイム ===
社員ID: P001
名前: 佐藤花子
給与: 96,000円

📝 実践例3:ショッピングシステム

通常商品とセール商品で価格計算を変えます。

完成コード:ショッピングシステム

※スマートフォンでは横スクロールできます

# 親クラス:商品
class Product:
    def __init__(self, name, price, stock):
        self.name = name
        self.price = price
        self.stock = stock
    
    def get_price(self):
        return self.price
    
    def can_purchase(self, quantity):
        return self.stock >= quantity
    
    def purchase(self, quantity):
        if self.can_purchase(quantity):
            self.stock -= quantity
            return True
        return False
    
    def __str__(self):
        return f"{self.name}: {self.price}円 (在庫: {self.stock}個)"

# 子クラス:セール商品
class DiscountProduct(Product):
    def __init__(self, name, price, stock, discount_rate):
        super().__init__(name, price, stock)
        self.discount_rate = discount_rate  # 割引率(例:0.2 = 20%OFF)
    
    def get_price(self):
        # 割引後の価格を返す(オーバーライド)
        return int(self.price * (1 - self.discount_rate))
    
    def __str__(self):
        original = super().__str__()
        discounted = self.get_price()
        return f"{original} → セール価格: {discounted}円"

# ショッピングカート
class ShoppingCart:
    def __init__(self):
        self.items = []
    
    def add_item(self, product, quantity):
        if product.can_purchase(quantity):
            self.items.append({"product": product, "quantity": quantity})
            product.purchase(quantity)
            print(f"{product.name} × {quantity}をカートに追加")
        else:
            print(f"{product.name}の在庫が不足しています")
    
    def checkout(self):
        print("\n=== お会計 ===")
        total = 0
        for item in self.items:
            product = item["product"]
            quantity = item["quantity"]
            subtotal = product.get_price() * quantity
            print(f"{product.name} × {quantity}: {subtotal}円")
            total += subtotal
        print(f"合計: {total}円")

# 使用例
apple = Product("りんご", 150, 10)
banana = DiscountProduct("バナナ", 200, 5, 0.2)  # 20%OFF

print(apple)
print(banana)

cart = ShoppingCart()
cart.add_item(apple, 3)
cart.add_item(banana, 2)
cart.checkout()

実行結果

りんご: 150円 (在庫: 10個)
バナナ: 200円 (在庫: 5個) → セール価格: 160円
りんご × 3をカートに追加
バナナ × 2をカートに追加

=== お会計 ===
りんご × 3: 450円
バナナ × 2: 320円
合計: 770円

💡 実践例のポイント

クラス変数:口座番号の自動採番(全口座で共有)

継承:共通機能を親クラスに、独自機能を子クラスに

オーバーライド:calculate_salary()、get_price()で計算方法を変更

super():親クラスの初期化を忘れずに呼び出す

📝 練習問題(12問)

ここまで学んだことを実際に手を動かして確認しましょう。

問題1:クラス変数でカウント(初級)

📋 問題

インスタンスが作られた回数をカウントするCounterクラスを作りましょう。

解答を見る

コード

※スマートフォンでは横スクロールできます

class Counter:
    count = 0  # クラス変数
    
    def __init__(self):
        Counter.count += 1
    
    @classmethod
    def get_count(cls):
        return cls.count

c1 = Counter()
c2 = Counter()
c3 = Counter()

print(f"作成されたインスタンス数: {Counter.get_count()}")

問題2:簡単な継承(初級)

📋 問題

Vehicleクラスを継承してCarクラスを作りましょう。Carクラスには独自のメソッドを追加します。

解答を見る

コード

※スマートフォンでは横スクロールできます

class Vehicle:
    def __init__(self, name):
        self.name = name
    
    def move(self):
        print(f"{self.name}が移動しています")

class Car(Vehicle):
    def honk(self):
        print(f"{self.name}: プップー!")

car = Car("マイカー")
car.move()   # 親クラスのメソッド
car.honk()   # 子クラスのメソッド

問題3:メソッドのオーバーライド(初級)

📋 問題

Shapeクラスを継承してCircleとRectangleクラスを作り、それぞれでarea()メソッドをオーバーライドしましょう。

解答を見る

コード

※スマートフォンでは横スクロールできます

class Shape:
    def area(self):
        return 0

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius
    
    def area(self):
        return 3.14 * self.radius ** 2

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    def area(self):
        return self.width * self.height

circle = Circle(5)
rectangle = Rectangle(4, 6)

print(f"円の面積: {circle.area()}")
print(f"長方形の面積: {rectangle.area()}")

問題4:従業員管理システム(中級)

📋 問題

Employeeクラスを継承して、FullTimeとPartTimeの2つのクラスを作りましょう。給与計算メソッドをそれぞれ実装します。

解答を見る

コード

※スマートフォンでは横スクロールできます

class Employee:
    def __init__(self, name):
        self.name = name
    
    def calculate_salary(self):
        return 0

class FullTime(Employee):
    def __init__(self, name, monthly_salary):
        super().__init__(name)
        self.monthly_salary = monthly_salary
    
    def calculate_salary(self):
        return self.monthly_salary

class PartTime(Employee):
    def __init__(self, name, hourly_rate, hours):
        super().__init__(name)
        self.hourly_rate = hourly_rate
        self.hours = hours
    
    def calculate_salary(self):
        return self.hourly_rate * self.hours

full = FullTime("太郎", 300000)
part = PartTime("花子", 1200, 80)

print(f"{full.name}の給与: {full.calculate_salary():,}円")
print(f"{part.name}の給与: {part.calculate_salary():,}円")

問題5:学生成績管理(中級)

📋 問題

Personクラスを継承してStudentクラスを作り、複数の成績を管理できるようにしましょう。

解答を見る

コード

※スマートフォンでは横スクロールできます

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

class Student(Person):
    def __init__(self, name, age, student_id):
        super().__init__(name, age)
        self.student_id = student_id
        self.grades = {}
    
    def add_grade(self, subject, score):
        self.grades[subject] = score
    
    def get_average(self):
        if not self.grades:
            return 0
        return sum(self.grades.values()) / len(self.grades)
    
    def show_report(self):
        print(f"学生ID: {self.student_id}")
        print(f"名前: {self.name} ({self.age}歳)")
        for subject, score in self.grades.items():
            print(f"{subject}: {score}点")
        print(f"平均点: {self.get_average():.1f}点")

student = Student("太郎", 15, "S001")
student.add_grade("数学", 85)
student.add_grade("英語", 92)
student.add_grade("国語", 78)
student.show_report()

問題6:図形の面積比較(中級)

📋 問題

複数の図形を作成し、面積が最も大きい図形を見つけるプログラムを作りましょう。

解答を見る

コード

※スマートフォンでは横スクロールできます

class Shape:
    def __init__(self, name):
        self.name = name
    
    def area(self):
        return 0

class Circle(Shape):
    def __init__(self, radius):
        super().__init__(f"円(半径{radius})")
        self.radius = radius
    
    def area(self):
        return 3.14 * self.radius ** 2

class Rectangle(Shape):
    def __init__(self, width, height):
        super().__init__(f"長方形({width}×{height})")
        self.width = width
        self.height = height
    
    def area(self):
        return self.width * self.height

shapes = [
    Circle(5),
    Rectangle(4, 6),
    Circle(3),
    Rectangle(10, 2)
]

max_shape = shapes[0]
for shape in shapes:
    print(f"{shape.name}: 面積={shape.area()}")
    if shape.area() > max_shape.area():
        max_shape = shape

print(f"\n最大面積: {max_shape.name} ({max_shape.area()})")

問題7:動物園シミュレーター(上級)

📋 問題

Animalクラスを継承して複数の動物クラスを作り、動物園で管理するプログラムを作りましょう。

解答を見る

コード

※スマートフォンでは横スクロールできます

class Animal:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def speak(self):
        print(f"{self.name}が鳴いています")

class Lion(Animal):
    def speak(self):
        print(f"{self.name}: ガオー!")

class Elephant(Animal):
    def speak(self):
        print(f"{self.name}: パオーン!")

class Penguin(Animal):
    def speak(self):
        print(f"{self.name}: ペペペッ!")

class Zoo:
    def __init__(self, name):
        self.name = name
        self.animals = []
    
    def add_animal(self, animal):
        self.animals.append(animal)
        print(f"{animal.name}を追加しました")
    
    def show_all_animals(self):
        print(f"\n=== {self.name} ===")
        for animal in self.animals:
            animal.speak()

zoo = Zoo("わくわく動物園")
zoo.add_animal(Lion("レオ", 5))
zoo.add_animal(Elephant("ゾウ子", 8))
zoo.add_animal(Penguin("ペンタ", 3))
zoo.show_all_animals()

問題8:タスク管理システム(上級)

📋 問題

Taskクラスを継承して、重要度別のタスククラス(UrgentTask、NormalTask)を作りましょう。

解答を見る

コード

※スマートフォンでは横スクロールできます

class Task:
    def __init__(self, title, description):
        self.title = title
        self.description = description
        self.completed = False
    
    def complete(self):
        self.completed = True
        print(f"「{self.title}」を完了しました")
    
    def __str__(self):
        status = "✓" if self.completed else " "
        return f"[{status}] {self.title}"

class UrgentTask(Task):
    def __init__(self, title, description, deadline):
        super().__init__(title, description)
        self.deadline = deadline
    
    def __str__(self):
        return f"🔴 {super().__str__()} (期限: {self.deadline})"

class NormalTask(Task):
    def __str__(self):
        return f"🟢 {super().__str__()}"

tasks = [
    UrgentTask("レポート提出", "数学のレポート", "明日"),
    NormalTask("買い物", "牛乳を買う"),
    UrgentTask("宿題", "英語の宿題", "今週中")
]

print("=== タスク一覧 ===")
for task in tasks:
    print(task)

tasks[1].complete()
print("\n=== 更新後 ===")
for task in tasks:
    print(task)

問題9:ゲームのアイテムシステム(上級)

📋 問題

Itemクラスを継承して、武器(Weapon)と防具(Armor)のクラスを作りましょう。

解答を見る

コード

※スマートフォンでは横スクロールできます

class Item:
    def __init__(self, name, price):
        self.name = name
        self.price = price
    
    def __str__(self):
        return f"{self.name} ({self.price}G)"

class Weapon(Item):
    def __init__(self, name, price, attack):
        super().__init__(name, price)
        self.attack = attack
    
    def __str__(self):
        return f"⚔️ {super().__str__()} 攻撃力+{self.attack}"

class Armor(Item):
    def __init__(self, name, price, defense):
        super().__init__(name, price)
        self.defense = defense
    
    def __str__(self):
        return f"🛡️ {super().__str__()} 防御力+{self.defense}"

class Inventory:
    def __init__(self):
        self.items = []
        self.gold = 1000
    
    def buy_item(self, item):
        if self.gold >= item.price:
            self.items.append(item)
            self.gold -= item.price
            print(f"{item.name}を購入しました (残り{self.gold}G)")
        else:
            print("お金が足りません!")
    
    def show_items(self):
        print("\n=== 所持アイテム ===")
        for item in self.items:
            print(item)
        print(f"所持金: {self.gold}G")

inventory = Inventory()
inventory.buy_item(Weapon("鉄の剣", 500, 10))
inventory.buy_item(Armor("革の鎧", 300, 5))
inventory.show_items()

問題10:レストラン注文システム(上級)

📋 問題

MenuItemクラスを継承して、Drink(飲み物)とFood(食べ物)クラスを作り、注文を管理しましょう。

解答を見る

コード

※スマートフォンでは横スクロールできます

class MenuItem:
    def __init__(self, name, price):
        self.name = name
        self.price = price
    
    def __str__(self):
        return f"{self.name}: {self.price}円"

class Drink(MenuItem):
    def __init__(self, name, price, size):
        super().__init__(name, price)
        self.size = size
    
    def __str__(self):
        return f"🥤 {super().__str__()} ({self.size})"

class Food(MenuItem):
    def __init__(self, name, price, calories):
        super().__init__(name, price)
        self.calories = calories
    
    def __str__(self):
        return f"🍔 {super().__str__()} ({self.calories}kcal)"

class Order:
    order_count = 0
    
    def __init__(self):
        Order.order_count += 1
        self.order_number = Order.order_count
        self.items = []
    
    def add_item(self, item):
        self.items.append(item)
        print(f"{item.name}を追加しました")
    
    def show_order(self):
        print(f"\n=== 注文番号: {self.order_number} ===")
        for item in self.items:
            print(item)
        total = sum(item.price for item in self.items)
        print(f"合計: {total}円")

order = Order()
order.add_item(Food("ハンバーガー", 500, 600))
order.add_item(Drink("コーラ", 200, "M"))
order.add_item(Food("ポテト", 300, 400))
order.show_order()

問題11:ポイントカードシステム(上級)

📋 問題

通常会員とプレミアム会員で異なるポイント還元率を持つシステムを作りましょう。

解答を見る

コード

※スマートフォンでは横スクロールできます

class Member:
    def __init__(self, name):
        self.name = name
        self.points = 0
    
    def earn_points(self, amount):
        points = int(amount * 0.01)  # 1%還元
        self.points += points
        print(f"{points}ポイント獲得!")
    
    def show_points(self):
        print(f"{self.name}さんのポイント: {self.points}P")

class PremiumMember(Member):
    def __init__(self, name):
        super().__init__(name)
        self.discount_rate = 0.1  # 10%割引
    
    def earn_points(self, amount):
        points = int(amount * 0.05)  # 5%還元
        self.points += points
        print(f"{points}ポイント獲得! (プレミアム会員)")
    
    def apply_discount(self, amount):
        return int(amount * (1 - self.discount_rate))

print("=== 通常会員 ===")
regular = Member("太郎")
regular.earn_points(10000)
regular.show_points()

print("\n=== プレミアム会員 ===")
premium = PremiumMember("花子")
original = 10000
discounted = premium.apply_discount(original)
print(f"割引後: {discounted}円")
premium.earn_points(discounted)
premium.show_points()

問題12:図書館管理システム(上級)

📋 問題

本と会員を管理し、貸出・返却ができるシステムを作りましょう。

解答を見る

コード

※スマートフォンでは横スクロールできます

class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author
        self.is_borrowed = False
    
    def __str__(self):
        status = "貸出中" if self.is_borrowed else "在庫あり"
        return f"『{self.title}』{self.author}著 [{status}]"

class LibraryMember:
    def __init__(self, name):
        self.name = name
        self.borrowed_books = []
    
    def borrow_book(self, book):
        if not book.is_borrowed:
            book.is_borrowed = True
            self.borrowed_books.append(book)
            print(f"{self.name}さんが『{book.title}』を借りました")
        else:
            print(f"『{book.title}』は貸出中です")
    
    def return_book(self, book):
        if book in self.borrowed_books:
            book.is_borrowed = False
            self.borrowed_books.remove(book)
            print(f"{self.name}さんが『{book.title}』を返却しました")
    
    def show_borrowed(self):
        print(f"\n{self.name}さんの借りている本:")
        if self.borrowed_books:
            for book in self.borrowed_books:
                print(f"  - {book.title}")
        else:
            print("  なし")

book1 = Book("Pythonの教科書", "山田太郎")
book2 = Book("データ分析入門", "佐藤花子")

member = LibraryMember("太郎")
member.borrow_book(book1)
member.borrow_book(book2)
member.show_borrowed()
member.return_book(book1)
member.show_borrowed()

❓ よくある質問

Q1: 継承はいつ使うべき?

〇〇は××の一種である」という関係がある時に使います:

・犬は動物の一種 → Animal を継承して Dog を作る

・正社員は従業員の一種 → Employee を継承して FullTime を作る

・貯蓄口座は銀行口座の一種 → BankAccount を継承して SavingsAccount を作る

共通部分を再利用することで、コードの重複を減らせます。

Q2: super()はなぜ必要?

親クラスのメソッドを呼び出すために使います。

特に__init__では、親クラスの初期化処理を実行するために重要です。

super()を忘れると、親クラスの属性が設定されずエラーになります。

Q3: クラス変数はいつ使う?

すべてのインスタンスで共有したいデータに使います:

・インスタンスの総数をカウントする

・全インスタンスで共通の設定値

・全インスタンスで共通の定数

インスタンスごとに異なる値が必要な場合は、インスタンス変数を使います。

Q4: 多重継承はできる?

Pythonでは可能ですが、複雑になりやすいです。

例:class A(B, C): で B と C を同時に継承

初心者は避けた方が良いでしょう。まずは単一継承に慣れましょう。

Q5: プライベート変数はある?

変数名を__(アンダースコア2つ)で始めると、外部からアクセスしにくくなります。

例:self.__balance

ただし、Pythonでは完全にプライベートにすることはできません(名前修飾でアクセス可能)。

慣例として_(アンダースコア1つ)で始めると「内部用」という意味になります。

📌 継承を使うべき場面

場面 親クラス 子クラス
動物の種類 Animal Dog, Cat, Bird
従業員の種類 Employee FullTime, PartTime
図形の種類 Shape Circle, Rectangle
商品の種類 Product DiscountProduct, NewProduct
口座の種類 BankAccount SavingsAccount, CheckingAccount

🎉 ステップ23のまとめ

✅ このステップで学んだこと

クラス変数:全インスタンスで共有される変数

インスタンス変数:各インスタンスごとに独立した変数

継承:既存クラスを元に新しいクラスを作る

オーバーライド:親クラスのメソッドを子クラスで上書き

super():親クラスのメソッドを呼び出す

✓ 実践的なクラス設計(銀行、従業員、ショッピング)

📌 クラスの活用まとめ

機能 書き方 用途
クラス変数 クラス直下に定義 全インスタンスで共有
継承 class 子(親): クラスの拡張
オーバーライド 同名メソッドを再定義 動作の変更
super().__init__() __init__内で呼ぶ 親クラスの初期化
@classmethod デコレータを付ける クラス変数へのアクセス

💪 次のステップへ

クラスの活用をマスターしました!

これで、継承やクラス変数を使った本格的なオブジェクト指向プログラミングができるようになりました。

オブジェクト指向入門編はここまでです。

次のステップからは、「実践プロジェクト編」に入ります。

これまで学んだことを総動員して、実際に動くプログラムを作りましょう!

📝

学習メモ

Pythonプログラミング基礎 - Step 23

📋 過去のメモ一覧
#artnasekai #学習メモ
LINE