Python 速習チュートリアル

Python のカプセル化

1. Pythonのカプセル化

カプセル化(Encapsulation)とは、クラス内部のデータを保護するための仕組みです。
これは、データ(プロパティ)とメソッドを一つのクラスとしてまとめると同時に、クラスの外部からそのデータにどのようにアクセスするかを制御することを意味します。

カプセル化を行うことで、データの予期せぬ変更を防ぎ、クラスが内部でどのように動作しているかという詳細を隠蔽(情報隠蔽)することができます。

2. プライベートプロパティ

Pythonでは、プロパティ名の前にダブルアンダースコア __ を付けることで、そのプロパティをプライベート(Private)に設定できます。

2.1 プライベートプロパティの作成例

__age という名前のプライベートプロパティを持つクラスを作成します。

class Person:
  def __init__(self, name, age):
    self.name = name
    self.__age = age # プライベートプロパティ

p1 = Person("エミル", 25)
print(p1.name)
# print(p1.__age) # これを実行するとエラー(AttributeError)が発生します

注意点: プライベートプロパティは、クラスの外部から直接アクセスすることはできません。

3. プライベートプロパティの値の取得

プライベートプロパティにアクセスするためには、ゲッター(Getter)と呼ばれるメソッドを作成します。

3.1 ゲッターメソッドの使用例

ゲッターを使用してプライベートプロパティの値を取得します。

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

  def get_age(self):
    # プライベートプロパティの値を返す
    return self.__age

p1 = Person("トビアス", 25)
print(p1.get_age())

4. プライベートプロパティの値の設定

プライベートプロパティの値を変更するには、セッター(Setter)メソッドを作成します。
セッターメソッド内では、値を設定する前にその値が妥当かどうかをチェック(バリデーション)することも可能です。

4.1 セッターメソッドの使用例

セッターを使用してプライベートプロパティの値を安全に変更します。

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

  def get_age(self):
    return self.__age

  def set_age(self, age):
    # 値のバリデーション(検証)
    if age > 0:
      self.__age = age
    else:
      print("年齢は正の数である必要があります")

p1 = Person("トビアス", 25)
print(p1.get_age())

p1.set_age(26)
print(p1.get_age())

5. なぜカプセル化を使うのか?

カプセル化には以下のような多くのメリットがあります。

  • データ保護(Data Protection): データの予期せぬ書き換えを防止します。
  • バリデーション(Validation): 値を設定する前に妥当性を検証できます。
  • 柔軟性(Flexibility): 外部のコードに影響を与えることなく、クラス内部のロジックを変更できます。
  • コントロール(Control): データの参照や変更の方法を完全に制御できます。

5.1 データの保護とバリデーションの実践例

class Student:
  def __init__(self, name):
    self.name = name
    self.__grade = 0

  def set_grade(self, grade):
    # 0から100の範囲内かチェック
    if 0 <= grade <= 100:
      self.__grade = grade
    else:
      print("グレードは0から100の間で指定してください")

  def get_grade(self):
    return self.__grade

  def get_status(self):
    if self.__grade >= 60:
      return "合格"
    else:
      return "不合格"

student = Student("エミル")
student.set_grade(85)
print(student.get_grade())
print(student.get_status())

6. プロテクテッドプロパティ

Pythonには、アンダースコア一つ _ をプレフィックス(接頭辞)として使用するプロテクテッド(Protected)プロパティの慣習もあります。

6.1 プロテクテッドプロパティの例

class Person:
  def __init__(self, name, salary):
    self.name = name
    self._salary = salary # プロテクテッドプロパティ

p1 = Person("ライナス", 50000)
print(p1.name)
print(p1._salary) # アクセス自体は可能だが、推奨されない

補足: シングルアンダースコア _ はあくまで慣習です。他の開発者に対して「このプロパティは内部用である」と伝えるためのサインであり、Python言語としてアクセスを制限する強制力はありません。

7. プライベートメソッド

プロパティと同様に、メソッドの前にダブルアンダースコアを付けることで、メソッドをプライベートにすることも可能です。

7.1 プライベートメソッドの作成例

class Calculator:
  def __init__(self):
    self.result = 0

  def __validate(self, num):
    # 内部的な数値チェック用メソッド
    if not isinstance(num, (int, float)):
      return False
    return True

  def add(self, num):
    if self.__validate(num):
      self.result += num
    else:
      print("無効な数値です")

calc = Calculator()
calc.add(10)
calc.add(5)
print(calc.result)
# calc.__validate(5) # クラス外部から呼び出すとエラーになります

注意点: プライベートメソッドもクラスの外部から直接呼び出すことはできません。__validate メソッドはクラス内部の他のメソッドからのみ利用されます。

8. 名前修飾 (Name Mangling)

名前修飾(Name Mangling)とは、Pythonがプライベートプロパティやメソッドを実装するために行っている内部処理のことです。

ダブルアンダースコア __ が使われると、Pythonは内部的にその名前の前に _クラス名 を付与してリネームします。
例えば、Person クラス内の __age は、内部的には _Person__age という名前に変換されます。

8.1 名前修飾の動作を確認する例

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

p1 = Person("エミル", 30)

# 名前修飾された名前を使えば、実はアクセスできてしまう:
print(p1._Person__age) # ただし、非推奨なコードです!

プロのアドバイス: 名前修飾された形式を使ってプライベート変数にアクセスすることは可能ですが、決して推奨されません。それはカプセル化の目的を壊してしまい、コードの保守性を著しく低下させるからです。