Pythonの基礎

Pythonのオブジェクト指向プログラミング

OOPは、状態を持ち明確なインターフェースで振る舞いを提供するオブジェクトを中心にプログラムを構成します。

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

OOPは協調するオブジェクトとしてシステムをモデル化します。各オブジェクトが状態と許可された操作をまとめるため、大規模なプログラムでも責務が明確になります。

OOPの4つの基本原則

  • カプセル化:状態と振る舞いをまとめ、アクセスを制御します。
  • 抽象化:必要なインターフェースを示し、不要な実装詳細を隠します。
  • 継承:真のis-a関係がある場合に既存クラスを特殊化します。
  • ポリモーフィズム:異なるオブジェクトに同じ操作を適用し、それぞれの振る舞いを実行します。

この例の設計

  1. BankAccountが残高を所有し入金を検証します。
  2. SavingsAccountが基底クラスの契約を再利用します。
  3. month_endをオーバーライドして利息を適用します。
  4. ループはすべての口座を同じインターフェースで扱います。
設計のヒント: 単に別のオブジェクトを利用するだけなら合成を優先し、子が親を正しく置換できる場合だけ継承を使います。

基本例

コードを実行 →
main.py
class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self._balance = float(balance)

    @property
    def balance(self):
        return self._balance

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("Deposit must be positive")
        self._balance += amount

    def month_end(self):
        return "No interest"


class SavingsAccount(BankAccount):
    def __init__(self, owner, balance, interest_rate):
        super().__init__(owner, balance)
        self.interest_rate = interest_rate

    def month_end(self):
        self._balance *= 1 + self.interest_rate
        return "Interest applied"


accounts = [
    BankAccount("Ada", 100),
    SavingsAccount("Lin", 200, 0.05),
]
accounts[0].deposit(50)

for account in accounts:
    status = account.month_end()
    print(f"{account.owner}: {account.balance:.2f} ({status})")

期待される出力

Ada: 150.00 (No interest)
Lin: 210.00 (Interest applied)

OOPの4原則を実践する

これらの例では、Pythonでカプセル化、抽象化、継承、ポリモーフィズムを日常的なコードに適用する方法を示します。

カプセル化

コードを実行 →

状態と振る舞いをまとめ、アクセスを制御します。

encapsulation.py
class BankAccount:
    def __init__(self, balance=0):
        # Double underscores prevent accidental direct access.
        self.__balance = balance

    @property
    def balance(self):
        # Expose read access without exposing mutation.
        return self.__balance

    def deposit(self, amount):
        # Keep validation next to the state it protects.
        if amount <= 0:
            raise ValueError("Amount must be positive")
        self.__balance += amount


account = BankAccount(100)
account.deposit(50)
print(account.balance)  # 150

抽象化

コードを実行 →

必要なインターフェースを示し、不要な実装詳細を隠します。

abstraction.py
from abc import ABC, abstractmethod


class PaymentMethod(ABC):
    @abstractmethod
    def pay(self, amount):
        # Callers know what pay does, not how each provider does it.
        pass


class CreditCard(PaymentMethod):
    def pay(self, amount):
        # The concrete class hides its payment implementation.
        return f"Paid ${amount:.2f} by card"


payment = CreditCard()
print(payment.pay(25))

継承

コードを実行 →

真のis-a関係がある場合に既存クラスを特殊化します。

inheritance.py
class Employee:
    def __init__(self, name):
        self.name = name

    def describe(self):
        return self.name


class Developer(Employee):
    def __init__(self, name, language):
        # Reuse initialization from the parent class.
        super().__init__(name)
        self.language = language

    def describe(self):
        # Extend inherited behavior instead of duplicating it.
        return f"{super().describe()} writes {self.language}"


print(Developer("Ada", "Python").describe())

ポリモーフィズム

コードを実行 →

異なるオブジェクトに同じ操作を適用し、それぞれの振る舞いを実行します。

polymorphism.py
class EmailNotification:
    def send(self, message):
        return f"Email: {message}"


class SmsNotification:
    def send(self, message):
        return f"SMS: {message}"


def notify(channels, message):
    for channel in channels:
        # The same call selects behavior from each concrete object.
        print(channel.send(message))


channels = [EmailNotification(), SmsNotification()]
notify(channels, "Order shipped")

仕組み

BankAccountは残高をカプセル化します。SavingsAccountはそのインターフェースを継承してmonth_endをオーバーライドします。ループでは同じ方法で扱い、実行時に適切なメソッドが選択されます。

値を変更し、PythonをインストールせずにCodeUtilityオンラインPythonコンパイラで実行できます。

練習問題

入力値と境界ケースを変更し、より大きなデータでも動作を確認してください。

  1. 実行前に出力を予測する。
  2. 空または不正な入力を処理する。
  3. 処理を関数化し、テストケースを追加する。
Python IDEで実行 →