カプセル化
オブジェクト指向の重要概念の一つがカプセル化です。カプセル化とは、プログラムの外部からの操作を制御し、プログラムの独立性を保つための仕組みです。
テレビのリモコンに例えると、ユーザーはリモコンの単純なボタン操作によって、チャンネルを変える、ボリュームを下げるなどの操作ができます。ユーザーは内部の複雑なプログラムや回路を意識する必要はなく、誤った操作も起こしにくくなります。また、ユーザーが好き勝手にプログラムを書き換えたり、内部の回路に触れて改造したり、といった故障の原因を未然に防ぎます。
このようにオブジェクトの内部構造を隠蔽し、公開されたインターフェースによって外部からの操作を制御する仕組みがカプセル化です。使える機能を制限することで、ユーザーはどの機能を使えばいいかが分かりやすく、内部のプログラムに干渉しない・させないことで、大規模なプログラムでも複雑にならず、安定性が担保されます。
アクセス制御 プライベートとパブリック
オブジェクト指向プログラミングにおけるカプセル化は、オブジェクトの内部を**プライベート(非公開)とパブリック(公開)**に分けて設定することです。プライベートなデータやメソッドに外部から直接アクセスすることはできず、公開された専用のメソッドによってのみアクセス可能となります。
ただし、Pythonでは公式ドキュメントに**「データ隠蔽を補強するための機構はなにもありません」と記述されている通り、基本的にすべてのデータやメソッドは外部に公開**されており、プライベートなデータやメソッドは存在しません。
そのかわり、慣習的な命名規則として、**"_
"(アンダーバー)**から始まる変数名やメソッド名はプライベートとみなします。
【書式】慣習的な命名規則としてのプライベート化
_要素名 # 要素名の前にアンダーバーを1つつける
例えば、クラスの中で_foo
のような名前の変数やメソッドがあった場合、それは**「クラスの設計側だけが使うものであり、外部からはそれを利用しない」という決まり**です。あくまでもそうみなす、というだけなのでアクセス自体は可能です。
しかし、それでは困る場合があります。そこで、**ネームマングリング(難号化)**という機能を利用します。
マングリングは要素名の前に**"__
"(アンダーバー2つ)**をつけます。
【書式】ネームマングリングによるプライベート化
__要素名 # 要素名の前にアンダーバーを2つつける
これによってクラスの外部から隠蔽し、プライベート化することができます。(実際には、非推奨の方法でアクセスすることもできます。)
プログラムで確認してみましょう。
次のプログラムでは、クラス変数を参照して戻り値を返すhello()
メソッドと、その戻り値をprint()
関数で表示するprinthello()
メソッドが定義されています。そして、3種類の方法でクラス変数へアクセスしています。
まずはプライベート化していない状態です。
class Sample:
name = "Yamada"
def hello(self):
return "Hello! I'm " + self.name
def printhello(self):
print(self.hello())
a = Sample()
print(a.name) # クラス変数nameを直接参照して表示
print(a.hello()) # hello()メソッドの戻り値を表示
a.printhello() # printhello()メソッドを呼び出す
実行してみましょう。
Yamada
Hello! I'm Yamada
Hello! I'm Yamada
では、クラス変数name
をマングリングして、プライベート変数__name
にアクセスできるか試してみましょう。
class Sample:
__name = "Yamada" # プライベート変数で定義
def hello(self):
return "Hello! I'm " + self.__name
def printhello(self):
print(self.hello())
a = Sample()
print(a.__name) # クラス変数__nameを直接参照して表示
print(a.hello())
a.printhello()
変数name
をプライベート変数__name
に変更しました。実行してみましょう。
Traceback (most recent call last):
File "private_sample.py", line 10, in <module>
print(a.__name)
AttributeError: 'Sample' object has no attribute '__name'
10行目print(a.__name)
の部分でAttributeError(属性エラー)「__name
という属性はありません」というエラーが出ました。変数__name
は隠蔽されているため、参照できなかったようです。
では、メソッド内で__name
を参照するhello()
メソッドはどうでしょうか?
エラーが出た10行目print(a.__name)
の部分をコメントアウト("#
"を付けてコメントにする)して確認しましょう。
class Sample:
__name = "Yamada" # プライベート変数で定義
def hello(self):
return "Hello! I'm " + self.__name
def printhello(self):
print(self.hello())
a = Sample()
# print(a.__name) コメントアウト
print(a.hello())
a.printhello()
実行してみましょう。
Hello! I'm Yamada
Hello! I'm Yamada
プライベート変数を直接参照することはできませんが、メソッドを通せばアクセス可能です。
次は、hello()
メソッドをプライベートメソッド__hello()
に変えてみましょう。
class Sample:
__name = "Yamada" # プライベート変数で定義
def __hello(self): # プライベートメソッドで定義
return "Hello! I'm " + self.__name
def printhello(self):
print(self.__hello())
a = Sample()
# print(a.__name) コメントアウト
print(a.__hello())
a.printhello()
実行してみましょう。
Traceback (most recent call last):
File "private_sample.py", line 13, in <module>
print(a.__hello())
AttributeError: 'Sample' object has no attribute '__hello'
11行目print(a.__hello())
の部分でエラーが出ました。__hello()
メソッドが隠蔽されているため、アクセスできなかったようです。
では、先ほどと同様にエラーが出た11行目print(a.__hello())
をコメントアウトして、メソッド内で__hello()
メソッドの戻り値を表示するprint_hello()
メソッドのほうを確認しましょう。
class Sample:
__name = "Yamada" # プライベート変数で定義
def __hello(self): # プライベートメソッドで定義
return "Hello! I'm " + self.__name
def printhello(self):
print(self.__hello())
a = Sample()
# print(a.__name) コメントアウト
# print(a.__hello()) コメントアウト
a.printhello()
実行してみましょう。
Hello! I'm Yamada
print_hello()
メソッドが実行できました。
このように、プライベート化によって変数やメソッドを隠蔽することで、外部からのアクセスを特定のメソッドに制限できました。
クラス内部の変数やメソッドをプライベートとパブリックに分けて適切に管理することで、クラスの保守性を高めることができます。しかし、クラスの保守だけがカプセル化の目的ではありません。次は、アクセス制御するメソッドについて掘り下げていきましょう。
初学者のためのPython講座 オブジェクト指向編1 オブジェクト指向とは
初学者のためのPython講座 オブジェクト指向編2 クラスとインスタンス
初学者のためのPython講座 オブジェクト指向編3 メソッド
初学者のためのPython講座 オブジェクト指向編4 初期化メソッド
初学者のためのPython講座 オブジェクト指向編5 クラスの継承
初学者のためのPython講座 オブジェクト指向編6 クラス変数とクラスメソッド
初学者のためのPython講座 オブジェクト指向編7 カプセル化
随時追加予定
次回「プロパティ」