クラスとオブジェクト

オブジェクト指向プログラミング入門

クラスとオブジェクト

オブジェクト指向プログラミングとは?

  • データ(属性)と機能(メソッド)を組み合わせる プログラミングパラダイム
  • 現実世界のオブジェクトをモデル化 - 車、人、銀行口座など
  • 再利用可能で保守性の高いコード を作成
  • カプセル化、継承、多態性 の概念を提供
# クラス = 設計図
class Car:
    def __init__(self, brand, model):
        self.brand = brand    # 属性
        self.model = model    # 属性
    
    def start(self):          # メソッド
        print(f"{self.brand} {self.model} エンジンスタート!")

# オブジェクト = インスタンス
my_car = Car("Toyota", "Prius")
my_car.start()  # Toyota Prius エンジンスタート!

クラスの基本構文

class Person:
    # クラス変数(すべてのインスタンスで共有)
    species = "Homo sapiens"
    
    def __init__(self, name, age):
        # インスタンス変数(各インスタンス固有)
        self.name = name
        self.age = age
    
    def introduce(self):
        # インスタンスメソッド
        return f"こんにちは、{self.name}です。{self.age}歳です。"
    
    def have_birthday(self):
        self.age += 1
        print(f"誕生日おめでとう!現在{self.age}歳です。")

# インスタンスの作成
person1 = Person("太郎", 25)
person2 = Person("花子", 30)

print(person1.introduce())  # こんにちは、太郎です。25歳です。
person1.have_birthday()     # 誕生日おめでとう!現在26歳です。

init メソッド(コンストラクタ)

class BankAccount:
    def __init__(self, account_number, initial_balance=0):
        self.account_number = account_number
        self.balance = initial_balance
        self.transaction_history = []
        
        # 初期化時のロジック
        if initial_balance > 0:
            self.transaction_history.append(f"初期入金: {initial_balance}円")
    
    def __str__(self):
        # オブジェクトを文字列として表現
        return f"口座番号: {self.account_number}, 残高: {self.balance}円"
    
    def __repr__(self):
        # オブジェクトの詳細表現(開発者向け)
        return f"BankAccount('{self.account_number}', {self.balance})"

# 使用例
account = BankAccount("123-456", 1000)
print(account)        # 口座番号: 123-456, 残高: 1000円
print(repr(account))  # BankAccount('123-456', 1000)

インスタンスメソッド

class Calculator:
    def __init__(self):
        self.history = []
    
    def add(self, a, b):
        result = a + b
        self._record_operation(f"{a} + {b} = {result}")
        return result
    
    def multiply(self, a, b):
        result = a * b
        self._record_operation(f"{a} × {b} = {result}")
        return result
    
    def _record_operation(self, operation):
        # プライベートメソッド(慣習的に_で始める)
        self.history.append(operation)
    
    def show_history(self):
        print("計算履歴:")
        for i, op in enumerate(self.history, 1):
            print(f"  {i}. {op}")

calc = Calculator()
calc.add(5, 3)
calc.multiply(4, 7)
calc.show_history()

クラス変数 vs インスタンス変数

class Student:
    # クラス変数 - すべてのインスタンスで共有
    school = "Python高校"
    total_students = 0
    
    def __init__(self, name, student_id):
        # インスタンス変数 - 各インスタンス固有
        self.name = name
        self.student_id = student_id
        self.grades = []
        
        # クラス変数を更新
        Student.total_students += 1
    
    @classmethod
    def get_total_students(cls):
        # クラスメソッド
        return cls.total_students
    
    @staticmethod
    def is_passing_grade(grade):
        # スタティックメソッド
        return grade >= 60

# 使用例
student1 = Student("太郎", "S001")
student2 = Student("花子", "S002")

print(f"学校: {Student.school}")                    # Python高校
print(f"総学生数: {Student.get_total_students()}")   # 2
print(f"合格?: {Student.is_passing_grade(75)}")    # True

プロパティ(getter/setter)

class Temperature:
    def __init__(self, celsius=0):
        self._celsius = celsius
    
    @property
    def celsius(self):
        """摂氏温度のgetter"""
        return self._celsius
    
    @celsius.setter
    def celsius(self, value):
        """摂氏温度のsetter"""
        if value < -273.15:
            raise ValueError("絶対零度を下回ることはできません")
        self._celsius = value
    
    @property
    def fahrenheit(self):
        """華氏温度(読み取り専用)"""
        return self._celsius * 9/5 + 32
    
    @property
    def kelvin(self):
        """ケルビン温度(読み取り専用)"""
        return self._celsius + 273.15

# 使用例
temp = Temperature(25)
print(f"摂氏: {temp.celsius}°C")      # 25°C
print(f"華氏: {temp.fahrenheit}°F")   # 77.0°F
print(f"ケルビン: {temp.kelvin}K")     # 298.15K

temp.celsius = 30  # setterが呼ばれる
# temp.celsius = -300  # ValueError!

特殊メソッド(Magic Methods)

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __str__(self):
        return f"Vector({self.x}, {self.y})"
    
    def __add__(self, other):
        # + 演算子をオーバーロード
        return Vector(self.x + other.x, self.y + other.y)
    
    def __mul__(self, scalar):
        # * 演算子をオーバーロード
        return Vector(self.x * scalar, self.y * scalar)
    
    def __len__(self):
        # len() 関数をサポート
        return int((self.x**2 + self.y**2)**0.5)
    
    def __eq__(self, other):
        # == 演算子をオーバーロード
        return self.x == other.x and self.y == other.y

# 使用例
v1 = Vector(3, 4)
v2 = Vector(1, 2)

print(v1 + v2)    # Vector(4, 6)
print(v1 * 2)     # Vector(6, 8)
print(len(v1))    # 5
print(v1 == v2)   # False

実践演習

演習: 図書館管理システム

class Book:
    def __init__(self, title, author, isbn):
        self.title = title
        self.author = author
        self.isbn = isbn
        self.is_borrowed = False
        self.borrower = None
    
    def __str__(self):
        status = "貸出中" if self.is_borrowed else "利用可能"
        return f"『{self.title}』- {self.author} [{status}]"

class Library:
    def __init__(self, name):
        self.name = name
        self.books = []
        self.members = []
    
    def add_book(self, book):
        self.books.append(book)
        print(f"本を追加しました: {book.title}")
    
    def register_member(self, member_name):
        if member_name not in self.members:
            self.members.append(member_name)
            print(f"会員登録しました: {member_name}")
    
    def borrow_book(self, isbn, member_name):
        if member_name not in self.members:
            print("会員登録が必要です")
            return False
        
        for book in self.books:
            if book.isbn == isbn:
                if not book.is_borrowed:
                    book.is_borrowed = True
                    book.borrower = member_name
                    print(f"貸出完了: {book.title}{member_name}")
                    return True
                else:
                    print(f"『{book.title}』は貸出中です")
                    return False
        
        print("指定されたISBNの本が見つかりません")
        return False
    
    def return_book(self, isbn):
        for book in self.books:
            if book.isbn == isbn and book.is_borrowed:
                borrower = book.borrower
                book.is_borrowed = False
                book.borrower = None
                print(f"返却完了: {book.title}{borrower}")
                return True
        print("貸出記録が見つかりません")
        return False
    
    def list_books(self):
        print(f"\n{self.name} 蔵書一覧:")
        for book in self.books:
            print(f"  {book}")

# 使用例
library = Library("市立図書館")

# 本を追加
book1 = Book("Python入門", "田中太郎", "978-1234567890")
book2 = Book("データ分析入門", "佐藤花子", "978-0987654321")

library.add_book(book1)
library.add_book(book2)

# 会員登録
library.register_member("山田一郎")
library.register_member("鈴木次郎")

# 貸出・返却
library.borrow_book("978-1234567890", "山田一郎")
library.list_books()
library.return_book("978-1234567890")
library.list_books()

演習: RPGキャラクター

import random

class Character:
    def __init__(self, name, hp=100, attack=20, defense=10):
        self.name = name
        self.max_hp = hp
        self.hp = hp
        self.attack = attack
        self.defense = defense
        self.level = 1
        self.experience = 0
    
    def take_damage(self, damage):
        actual_damage = max(0, damage - self.defense)
        self.hp = max(0, self.hp - actual_damage)
        print(f"{self.name}{actual_damage}のダメージを受けた!(残りHP: {self.hp})")
        
        if self.hp <= 0:
            print(f"{self.name}は倒れた...")
            return True
        return False
    
    def attack_target(self, target):
        # クリティカル判定
        is_critical = random.random() < 0.1  # 10%の確率
        damage = self.attack
        
        if is_critical:
            damage *= 2
            print(f"{self.name}のクリティカルヒット!")
        
        print(f"{self.name}の攻撃!")
        return target.take_damage(damage)
    
    def heal(self, amount):
        old_hp = self.hp
        self.hp = min(self.max_hp, self.hp + amount)
        healed = self.hp - old_hp
        print(f"{self.name}{healed}HP回復した!(現在HP: {self.hp})")
    
    def gain_experience(self, exp):
        self.experience += exp
        print(f"{self.name}{exp}の経験値を得た!")
        
        # レベルアップ判定
        exp_needed = self.level * 100
        if self.experience >= exp_needed:
            self.level_up()
    
    def level_up(self):
        self.level += 1
        self.max_hp += 10
        self.hp = self.max_hp
        self.attack += 5
        self.defense += 3
        print(f"🎉 {self.name}はレベル{self.level}に上がった!")
        print(f"   HP: {self.max_hp}, 攻撃力: {self.attack}, 防御力: {self.defense}")
    
    def __str__(self):
        return f"{self.name} (Lv.{self.level}) HP:{self.hp}/{self.max_hp}"

class Warrior(Character):
    def __init__(self, name):
        super().__init__(name, hp=120, attack=25, defense=15)
        self.special_attacks = 3
    
    def shield_bash(self, target):
        if self.special_attacks > 0:
            self.special_attacks -= 1
            damage = self.attack * 1.5
            print(f"{self.name}のシールドバッシュ!(残り{self.special_attacks}回)")
            return target.take_damage(damage)
        else:
            print("特殊攻撃の回数が残っていません")
            return self.attack_target(target)

class Mage(Character):
    def __init__(self, name):
        super().__init__(name, hp=80, attack=30, defense=5)
        self.magic_points = 50
    
    def fireball(self, target):
        if self.magic_points >= 10:
            self.magic_points -= 10
            damage = self.attack * 2
            print(f"{self.name}のファイアボール!(残りMP: {self.magic_points})")
            return target.take_damage(damage)
        else:
            print("MPが不足しています")
            return self.attack_target(target)
    
    def heal_spell(self):
        if self.magic_points >= 15:
            self.magic_points -= 15
            self.heal(30)
            print(f"(残りMP: {self.magic_points})")
        else:
            print("MPが不足しています")

# バトルシステム例
def battle(char1, char2):
    print(f"\n⚔️ {char1.name} vs {char2.name} ⚔️")
    
    turn = 1
    while char1.hp > 0 and char2.hp > 0:
        print(f"\n--- ターン {turn} ---")
        print(f"{char1} vs {char2}")
        
        # プレイヤー1の攻撃
        if isinstance(char1, Warrior) and char1.special_attacks > 0:
            char1.shield_bash(char2)
        elif isinstance(char1, Mage) and char1.magic_points >= 10:
            char1.fireball(char2)
        else:
            char1.attack_target(char2)
        
        if char2.hp <= 0:
            print(f"\n🏆 {char1.name}の勝利!")
            char1.gain_experience(50)
            break
        
        # プレイヤー2の攻撃
        char2.attack_target(char1)
        
        if char1.hp <= 0:
            print(f"\n🏆 {char2.name}の勝利!")
            char2.gain_experience(50)
            break
        
        turn += 1

# 使用例
warrior = Warrior("勇者")
mage = Mage("魔法使い")

print(warrior)
print(mage)

battle(warrior, mage)

重要ポイント

  1. クラス = 設計図、オブジェクト = インスタンス
  2. init でオブジェクトを初期化
  3. self で現在のインスタンスを参照
  4. プロパティ で属性へのアクセスを制御
  5. 特殊メソッド でPythonらしい動作を実現

次の内容

  • 継承 (クラスの拡張)
  • 多態性 (同じインターフェースで異なる動作)
  • 抽象クラス (共通インターフェースの定義)
  • デザインパターン (実装の知恵)

ありがとうございました!

質問はありますか?

次は 継承 に進みましょう!

リソース: - Python.org - クラス - Real Python - OOP - Python Tutor

ナビゲーション