【Python中級への道 #10】クラスのデータを守る「カプセル化」とは?アンダースコアの使い方も解説

ALL

こんにちは、Tech Samuraiです!
前回の記事「【Python中級への道 #9】OOPの真髄!「継承」でクラスの能力を引き継ぎ、進化させよう」では、継承を使ってクラスの能力を拡張する方法を学びましたね。

クラスを使ってデータ(属性)と処理(メソッド)をひとまとめにできるようになりましたが、実は今のままのクラスには、ある大きな弱点があります。それは、**クラスの外部から、重要なデータが誰でも簡単に書き換えられてしまう**ことです。

例えば、ゲームのプレイヤーのHPが、何かの間違いでマイナスの値になってしまったらどうでしょう?

player1 = Player("Tech Samurai", 100, 15)
# 外部から直接、不正な値を代入できてしまう!
player1.hp = -9999

これでは、ゲームのルールが崩壊してしまいます。今回のテーマである**「カプセル化 (Encapsulation)」**は、このような意図しないデータの破壊を防ぎ、オブジェクトを安全に保つための、OOPにおける非常に重要な防御壁の概念です。


カプセル化とは? データと処理を隠す金庫

カプセル化とは、オブジェクトの**データ(属性)を外部から直接アクセスできないように隠し、代わりに用意された特定の処理(メソッド)を通してのみ、データの読み書きを許可する**という考え方です。

例えるなら… 「銀行の金庫」です。

  • あなたのお金(データ)は、頑丈な金庫の中に安全に保管されています。誰もが勝手に金庫を開けて、あなたのお金を書き換えることはできません。
  • お金を出し入れしたいときは、正規の「窓口(メソッド)」に行き、「通帳と印鑑(正しい手順)」を提示して、「1万円預けたい」あるいは「5千円引き出したい」とお願いする必要があります。
  • 窓口の担当者は、そのお願いが妥当か(残高は足りているかなど)をチェックした上で、金庫の中のお金を操作します。

このように、データを「隠蔽」し、決められた手続き(メソッド)を通してのみアクセスを許可することで、データの整合性を保ち、オブジェクトを安全に利用できるようにするのがカプセル化の目的です。


Pythonにおけるカプセル化:アンダースコアの紳士協定

他のプログラミング言語では、データを隠すために`private`のような特別なキーワードを使いますが、Pythonでは**変数名の前にアンダースコア `_` を付ける**という、プログラマー同士の「紳士協定」でこれを表現します。

  • _変数名(アンダースコア1つ):
    「これはクラスの内部で使うための変数です。外部からは直接触らないでくださいね」という、プログラマーへの**弱いお願い**です。「保護された(Protected)」変数と呼ばれます。
  • __変数名(アンダースコア2つ):
    「これはクラスの外部からは絶対に見えないし、触れません!」という、より**強い意思表示**です。「プライベート(Private)」変数と呼ばれ、Pythonが裏側で名前を自動的に変更し、外部からのアクセスを困難にします。

今回は、より強力な__(アンダースコア2つ)を使って、プレイヤーのHPを外部から守ってみましょう。


ゲッターとセッターによる安全なアクセス

HPをプライベート変数 `__hp` にして隠してしまうと、外部からHPを読み書きできなくなります。そこで、先ほどの銀行の窓口の役割を果たす、専用のメソッドを用意します。

  • ゲッター (Getter): プライベート変数の値を**取得するため**のメソッド。
  • セッター (Setter): プライベート変数に値を**設定するため**のメソッド。値の妥当性チェックもここで行う。

`Player`クラスを、カプセル化を使って書き直してみましょう。

class Player:
    def __init__(self, name, hp, attack_power):
        self.name = name
        # HPをプライベート変数として定義。外部から直接アクセスできなくなる。
        self.__hp = hp 
        self.attack_power = attack_power

    # --- HPのためのゲッター ---
    def get_hp(self):
        """現在のHPを取得する"""
        return self.__hp

    # --- HPのためのセッター ---
    def set_hp(self, amount):
        """HPを設定する。0未満にならないようにチェックする。"""
        if amount < 0:
            self.__hp = 0
            print(f"{self.name}のHPは0になった!")
        else:
            self.__hp = amount

    def take_damage(self, damage):
        """ダメージを受ける処理"""
        print(f"{self.name} は {damage} のダメージを受けた!")
        new_hp = self.__hp - damage
        # HPの変更は、必ずセッターを経由して行う
        self.set_hp(new_hp)

# --- 使ってみよう ---
player1 = Player("Tech Samurai", 100, 15)

# HPを読み出すときは、ゲッターを使う
print(f"現在のHP: {player1.get_hp()}")

# ダメージを受ける
player1.take_damage(30)
print(f"現在のHP: {player1.get_hp()}")

# さらに大きなダメージを受ける
player1.take_damage(80)
print(f"現在のHP: {player1.get_hp()}") # 0未満にならず、0で止まる!

# 外部から直接マイナス値を代入しようとしても…
# player1.__hp = -9999 # このコードはエラーになるか、意図通りに動作しない

# HPを変更したい場合は、必ずセッターを使う
# player1.set_hp(50) 

実行結果:

現在のHP: 100
Tech Samurai は 30 のダメージを受けた!
現在のHP: 70
Tech Samurai は 80 のダメージを受けた!
Tech SamuraiのHPは0になった!
現在のHP: 0

take_damageメソッドでHPがマイナスになっても、set_hpメソッドのチェック機能によって、HPがきちんと`0`で止まっているのが分かりますね。これで、HPが不正な値になることを防ぎ、クラスの安全性が格段に向上しました。


まとめ

今回は、オブジェクト指向プログラミングの三大要素の最後の一つ、「カプセル化」について探検しました。

  • カプセル化は、オブジェクトの重要なデータを外部から守るための**「壁」**であること。
  • Pythonでは、変数名の前に**アンダースコア (_ or __)** を付けることで、内部用であることを示す。
  • **ゲッター**と**セッター**という専用のメソッドを用意することで、データの読み書きを安全に制御できること。

クラス、継承、そしてカプセル化。これであなたは、オブジェクト指向プログラミングの基本的なツールキットを全て手に入れました。あなたはもはや、単にスクリプトを書くだけでなく、堅牢で再利用性の高い、ソフトウェア「システム」を設計するための第一歩を踏み出したのです。

これにて、Python中級への道・OOP編は一区切りです。次回からは、これらの高度なスキルを武器に、さらにエキサイティングで複雑なプロジェクトに挑戦していきましょう!

コメント

タイトルとURLをコピーしました