【Python】クラスのカプセル化

【Python】クラスのカプセル化

目次

カプセル化とは

カプセル化(Encapsulation)は、オブジェクト指向プログラミングの重要な概念の一つであり、データ(属性)とメソッド(操作)をまとめ、外部からの直接的なアクセスを制限することを指します。これにより、データの不正な変更を防ぎ、コードの保守性や安全性を向上させることができます。

アクセス修飾子

Pythonでは、他の言語(JavaやC++)のような明示的なアクセス修飾子(public、private、protected)はありませんが、慣例的に以下の3つの種類を使い分けます。

  • パブリック(public): 制限なし(例: self.value
  • プロテクト(protected): クラス内部およびサブクラス内での使用を推奨(例: self._value
  • プライベート(private): クラス内部でのみアクセス可能(例: self.__value

例えば、以下のようなクラスが考えられます。

class Example:
    def __init__(self):
        self.public_var = "公開"
        self._protected_var = "保護"
        self.__private_var = "非公開"

obj = Example()
print(obj.public_var)    # OK
print(obj._protected_var) # OK(ただし慣例的に外部からのアクセスは避ける)
print(obj.__private_var)  # エラー(アクセス不可)

プライベート変数

Pythonでは、変数名の先頭にダブルアンダースコア(__)を付けることで、その変数をプライベートにできます。

class Sample:
    def __init__(self):
        self.__private_data = "秘密"

obj = Sample()
print(obj.__private_data)  # エラー(直接アクセス不可)

ただし、Pythonでは完全に隠蔽されるわけではなく、名前マングリングによってアクセスできます。

print(obj._Sample__private_data)  # "秘密"(アクセス可能だが推奨されない)

getterとsetter

プライベート変数にアクセスするための方法として、getter(取得)とsetter(設定)を使用するのが一般的です。

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

    def get_name(self):
        return self.__name

    def set_name(self, new_name):
        if new_name:
            self.__name = new_name

p = Person("Taro")
print(p.get_name())  # "Taro"
p.set_name("Jiro")
print(p.get_name())  # "Jiro"

プロパティ(@property)

Pythonでは、@propertyデコレーターを使ってgetterとsetterを定義できます。これにより、メソッドを変数のように扱えます。

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

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, new_name):
        if new_name:
            self.__name = new_name

p = Person("Taro")
print(p.name)  # "Taro"
p.name = "Jiro"
print(p.name)  # "Jiro"

@propertyを使うことで、外部からのアクセスは通常の変数のように見えますが、内部ではgetter/setterが動作しているため、データの制御が可能です。

名前マングリング

プライベート変数(__var)は名前マングリングにより、実際には_ClassName__varの形式になります。

class Test:
    def __init__(self):
        self.__secret = "秘密"

obj = Test()
print(dir(obj))  # ['_Test__secret', ...]
print(obj._Test__secret)  # "秘密"

このようにアクセスは可能ですが、慣例的に使用すべきではありません。

カプセル化の利点

カプセル化には以下の利点があります。

  • データの保護: 外部からの不正な変更を防ぐ
  • コードの保守性向上: 内部の実装を隠蔽し、変更に強い設計が可能
  • 制約の設定: setterでバリデーションを行い、データの一貫性を保つ

例えば、年齢を負の値に設定できないようにすることができます。

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

    @property
    def age(self):
        return self.__age

    @age.setter
    def age(self, new_age):
        if new_age >= 0:
            self.__age = new_age

p = Person(-5)
print(p.age)  # 0
p.age = 30
print(p.age)  # 30
p.age = -10  # 無視される
print(p.age)  # 30

このように、カプセル化を適切に活用することで、より安全なコードを記述できます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です