【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
このように、カプセル化を適切に活用することで、より安全なコードを記述できます。