Python
初心者
Python3

初学者のためのPython講座 オブジェクト指向編7 カプセル化


カプセル化

オブジェクト指向の重要概念の一つがカプセル化です。カプセル化とは、プログラムの外部からの操作を制御し、プログラムの独立性を保つための仕組みです。

テレビのリモコンに例えると、ユーザーはリモコンの単純なボタン操作によって、チャンネルを変える、ボリュームを下げるなどの操作ができます。ユーザーは内部の複雑なプログラムや回路を意識する必要はなく、誤った操作も起こしにくくなります。また、ユーザーが好き勝手にプログラムを書き換えたり、内部の回路に触れて改造したり、といった故障の原因を未然に防ぎます。

このようにオブジェクトの内部構造を隠蔽し、公開されたインターフェースによって外部からの操作を制御する仕組みがカプセル化です。使える機能を制限することで、ユーザーはどの機能を使えばいいかが分かりやすく、内部のプログラムに干渉しない・させないことで、大規模なプログラムでも複雑にならず、安定性が担保されます。


アクセス制御 プライベートとパブリック

オブジェクト指向プログラミングにおけるカプセル化は、オブジェクトの内部をプライベート(非公開)パブリック(公開)に分けて設定することです。プライベートなデータやメソッドに外部から直接アクセスすることはできず、公開された専用のメソッドによってのみアクセス可能となります。

ただし、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 カプセル化

随時追加予定

次回「プロパティ」