オブジェクト指向プログラミング(OOP)において重要な考え方のひとつが「カプセル化」と「データ隠蔽」です。これらは、プログラムの信頼性を高め、誤った使い方による不具合を防ぐために欠かせない仕組みです。 Pythonではこれらをシンプルな記法で表現できるため、初心者でも実践しやすい特徴があります。本記事では、Pythonを使ってカプセル化とデータ隠蔽をわかりやすく解説し、実際のコード例を通じてその効果を確認していきましょう。
カプセル化とは、オブジェクトの「データ(属性)」と「操作(メソッド)」をひとまとまりにして管理し、必要以上に外部から触れられないようにする設計原則です。
例えば、人間を表すクラスを考えたとき、「名前」や「年齢」といった情報はデータであり、「挨拶する」などの動作はメソッドにあたります。
カプセル化によって、これらを一つのオブジェクトにまとめることで、使いやすく安全な仕組みを作ることができます。
Pythonでは、クラスを定義するだけで自然とデータとメソッドがセットになり、カプセル化の基本が実現されます。
データ隠蔽とは、オブジェクトが持つ内部データに直接アクセスできないように制御する仕組みのことです。
もしすべての属性を誰でも自由に変更できると、想定外の値が入り、プログラム全体が不安定になってしまいます。
例えば、銀行口座の残高を表す変数にマイナスの値を直接代入できてしまうと、実際の業務ではありえない不具合が生じます。
データ隠蔽は、このような不正な変更を防ぎ、オブジェクトの一貫性を守る役割を果たします。
Pythonには「アクセス修飾子」と呼ばれる仕組みがあり、属性やメソッドの見え方を調整することができます。
代表的なのは次の3種類です。
これらを使い分けることで、安全にデータを扱えるようになります。
まずは最も基本的なパブリック属性から見てみましょう。
以下のコードでは「名前」と「年齢」を属性として持つPersonクラスを定義しています。
class Person:
def __init__(self, name, age):
self.name = name # パブリック属性
self.age = age # パブリック属性
def greet(self):
print(f"こんにちは、私の名前は{self.name}です。年齢は{self.age}歳です。")
# オブジェクトの生成
person = Person("太郎", 25)
# パブリック属性へのアクセス
print(person.name) # 出力: 太郎
print(person.age) # 出力: 25
# パブリック属性の変更
person.age = 26
print(person.age) # 出力: 26
ここでは、person.name や person.age に直接アクセスできるため、簡単に値を参照・変更できます。 ただし、これは便利である反面、誤って不適切な値を代入してしまう危険性もあります。
次に、プロテクテッド属性です。
属性名の前にアンダースコア1つをつけることで、その変数は内部的に使うものという合図になります。
class Employee:
def __init__(self, name, salary):
self.name = name # パブリック属性
self._salary = salary # プロテクテッド属性
def get_salary(self):
return self._salary
def set_salary(self, amount):
if amount > 0:
self._salary = amount
else:
print("給与は正の数でなければなりません。")
#オブジェクトの生成
employee = Employee("花子", 50000)
# 非推奨だが直接アクセスは可能
print(employee._salary) # 50000
# メソッドを通じて安全にアクセス
print(employee.get_salary()) # 50000
# 値の変更
employee.set_salary(55000) # 正しく更新される
employee.set_salary(-1000) # エラーメッセージが表示される
このように、メソッドを経由して値を変更することで「給与は必ず正の数である」というルールを守ることができます。
さらに厳密にデータを隠蔽したい場合は、アンダースコアを2つ(__)をつけてプライベート属性にします。
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner # パブリック属性
self.__balance = balance # プライベート属性
def deposit(self, amount):
if amount > 0:
self.__balance += amount
print(f"{amount}円を入金しました。現在の残高は{self.__balance}円です。")
else:
print("入金額は正の数でなければなりません。")
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount
print(f"{amount}円を引き出しました。現在の残高は{self.__balance}円です。")
else:
print("引き出し額が不正です。")
def get_balance(self):
return self.__balance
# オブジェクトの生成
account = BankAccount("佐藤", 10000)
# プライベート属性へのアクセス(エラーが発生)
# print(account.__balance) # AttributeError: 'BankAccount' object has no attribute '__balance'
# メソッドを通じたアクセス
print(account.get_balance()) # 出力: 10000
# メソッドを通じた属性の変更
account.deposit(5000) # 出力: 5000円を入金しました。現在の残高は15000円です。
account.withdraw(2000) # 出力: 2000円を引き出しました。現在の残高は13000円です。
print(account.get_balance()) # 出力: 13000
ここでは __balanceに直接触れられないため、必ずdepositやwithdrawメソッドを通じて残高を変更する必要があります。 銀行口座のような厳密に守るべきデータを扱う際には、この方法がとても有効です。
名前マングリング(Name Mangling)とは、Pythonにおいてクラス定義内で特定の名前付け規則に従った属性やメソッド名が、実際には別の内部的な名前へと自動的に書き換えられる仕組みのことを指します。
特に、クラス内部で定義された名前が"ダブルアンダースコアで始まり、ダブルアンダースコアで終わらない"場合に、Pythonはその名前をクラス名で変換した別の名前(マングリング後の名前)として内部管理します。
class MyClass:
def __init__(self):
self.__value = 42
def __method(self):
return "secret method"
obj = MyClass()
print(dir(obj))
上記の場合、__value や __method はクラス定義内部では「__value」「__method」と書きますが、実際には、MyClass__value や MyClass__method といった名前に置き換えられます。 このような変換を名前マングリングと呼びます。
ここまで学習した内容をまとめます。
実際にコードを書いて試すことで、カプセル化とデータ隠蔽の概念とその実装方法をより深く理解しましょう。