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

# カプセル化

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

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

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

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

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

ただし、Pythonでは公式ドキュメントに**「データ隠蔽を補強するための機構はなにもありません」**と記述されている通り、基本的にすべてのデータやメソッドは外部に**公開**されており、**プライベートなデータやメソッドは存在しません**。


そのかわり、慣習的な命名規則として、**"`_`"（アンダーバー）**から始まる変数名やメソッド名はプライベートとみなします。

#### 【書式】慣習的な命名規則としてのプライベート化

```python
_要素名  # 要素名の前にアンダーバーを1つつける
```

 

例えば、クラスの中で`_foo`のような名前の変数やメソッドがあった場合、それは**「クラスの設計側だけが使うものであり、外部からはそれを利用しない」**という**決まり**です。あくまでもそうみなす、というだけなのでアクセス自体は可能です。

 

しかし、それでは困る場合があります。そこで、**ネームマングリング（難号化）**という機能を利用します。

マングリングは要素名の前に**"`__`"（アンダーバー2つ）**をつけます。

#### 【書式】ネームマングリングによるプライベート化

```Python
__要素名  # 要素名の前にアンダーバーを2つつける
```

 

これによってクラスの外部から隠蔽し、プライベート化することができます。（実際には、非推奨の方法でアクセスすることもできます。）

 

プログラムで確認してみましょう。

次のプログラムでは、クラス変数を参照して戻り値を返す`hello()`メソッドと、その戻り値を`print()`関数で表示する`printhello()`メソッドが定義されています。そして、3種類の方法でクラス変数へアクセスしています。

まずはプライベート化していない状態です。

```python
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()メソッドを呼び出す
```

実行してみましょう。

```tx
Yamada
Hello! I'm Yamada
Hello! I'm Yamada
```

では、クラス変数`name`をマングリングして、**プライベート変数**`__name`にアクセスできるか試してみましょう。

```python
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`に変更しました。実行してみましょう。

```tx
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)`の部分を**コメントアウト**（"`#`"を付けてコメントにする）して確認しましょう。

```python
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()
```

 実行してみましょう。

```txt
Hello! I'm Yamada
Hello! I'm Yamada
```

プライベート変数を直接参照することはできませんが、**メソッドを通せばアクセス可能**です。



次は、`hello()`メソッドを**プライベートメソッド**`__hello()`に変えてみましょう。

```python
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()
```
実行してみましょう。

```txt
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()`メソッドのほうを確認しましょう。

```python
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()
```

実行してみましょう。

```txt
Hello! I'm Yamada
```

`print_hello()`メソッドが実行できました。

このように、プライベート化によって変数やメソッドを隠蔽することで、**外部からのアクセスを特定のメソッドに制限**できました。

クラス内部の変数やメソッドをプライベートとパブリックに分けて適切に管理することで、クラスの保守性を高めることができます。しかし、クラスの保守だけがカプセル化の目的ではありません。次は、アクセス制御するメソッドについて掘り下げていきましょう。

***
[初学者のためのPython講座　オブジェクト指向編1　オブジェクト指向とは](https://qiita.com/kotakahe/items/9de3b515f8faf01510f0)
[初学者のためのPython講座　オブジェクト指向編2　クラスとインスタンス](https://qiita.com/kotakahe/items/3de8d471ea871b0a7024)
[初学者のためのPython講座　オブジェクト指向編3　メソッド](https://qiita.com/kotakahe/items/3360898ff84c2c141a1b)
[初学者のためのPython講座　オブジェクト指向編4　初期化メソッド](https://qiita.com/kotakahe/items/db3e9fa9d547bb6e9014)
[初学者のためのPython講座　オブジェクト指向編5　クラスの継承](https://qiita.com/kotakahe/items/b678250389af7fa885a5)
[初学者のためのPython講座　オブジェクト指向編6　クラス変数とクラスメソッド](https://qiita.com/kotakahe/items/e0aa41a08b31e91405b9)
初学者のためのPython講座　オブジェクト指向編7　カプセル化

随時追加予定
次回「プロパティ」