なぜPythonにはsetter/getterメソッドがないのか？

# 概要
JavaやC++などの他のオブジェクト指向言語では`setter/getter`メソッドの使用が推奨されているが、Pythonには存在しない。
もともとJavaやC++に触れていた僕には「**なぜPythonにはsetter/getterメソッドがないのか？**」がすごく疑問だった。
さらに、Javaなどの言語の資料では
「**クラス内部の変数はprivateにして隠蔽せよ**」
と書いてあるが、逆にPythonの資料では
「**privateはなるべく使わずpublicを使え**」
と書いてあることが多い。
**矛盾しているのでは...?**

気になったのでいろいろ調べて見た結果、そこには**２つの理由**があることがわかった。
さらに、Pythonで`setter/getter`を使いたい場合の**1つの代替案**があるということもわかった。
この記事では

- オブジェクト指向とカプセル化について
- Pythonにsetter/getterメソッドが存在しない2つの理由
- setter/getterメソッドの代替案

の順に説明する。
最初のオブジェクト指向についての説明については基礎的な内容なので、「**そんなの知ってるぜ！**」という人はすっ飛ばしてしまって大丈夫。

# オブジェクト指向とクラス
最近の言語ではオブジェクト指向言語と呼ばれるものが多く、クラスという概念がある。
Pythonで適当なクラスの例に考える。
以下のようなポケモンのクラスがあって名前と体重を設定可能とする。

``` Python
# クラスの定義
class Pokemon:
    def __init__(self, name='フシギダネ', weight=7.0):
        self.name = name
        self.weight = weight

# 1匹目 (デフォルトの名前)
pokemon = Pokemon()
print("このポケモンの名前は", pokemon.name, "だよ")
print("重さは", pokemon.weight, " kgだよ")

# ２匹目 (名前と体重を後から代入)
pokemon2 = Pokemon()
pokemon2.name = 'ピカチュウ'
pokemon2.weight = 6.0
print("2匹目のポケモンの名前は", pokemon2.name, "だよ")
print("重さは", pokemon2.weight, " kgだよ")

# 3匹目(コンストラクタでset)
pokemon3 = Pokemon('ポッポ',10)
print("3匹目のポケモンの名前は", pokemon3.name, "だよ")
print("重さは", pokemon3.weight, " kgだよ")
```

``` bash:出力
このポケモンの名前は フシギダネ だよ
重さは 7.0  kgだよ
2匹目のポケモンの名前は ピカチュウ だよ
重さは 6.0  kgだよ
3匹目のポケモンの名前は ポッポ だよ
重さは 10  kgだよ
```
`Pokemon()`クラスから具体的に3匹のポケモンの`オブジェクト` （Pythonでは`インスタンス`と言う）を生成している。

`__init__`がコンストラクタの部分。
この中でデフォルトの名前を`フシギダネ`にしてるので、特に名前を指定しなければ`フシギダネ`になる。
2匹目は名前と体重を代入してるので`ピカチュウ`になっている。
3匹目のようにコンストラクタの引数で直接設定することもできる。


しかし、このような書き方はいろいろと問題がある。
例えば、クラスの変数に外からいくらでもアクセス可能なため、**体重を変な値に書き換えることも簡単にできちゃう**。

``` python
pokemon2 = Pokemon()
pokemon2.name = 'ピカチュウ'
pokemon2.weight = '6.0'
pokemon2.weight = pokemon2.weight -9999 

print("重さは", pokemon2.weight, " kgだよ")
# 出力: 重さは -9993.0  kgだよ

```
そこで`setter`や`getter`メソッドを使った**カプセル化**の概念が出てくる。

# カプセル化とは
コードを使わずに少し抽象的な話をする。
例えば僕らが洗濯機を使う時を考える。
僕らは「**スタート**」のボタンを押せば洗濯が始まるということは知ってるけど、中身の配線や細かい原理がどうなってるかはよく知らない。
というか、知る必要がない。
「**ボタンを押す**」という僕らの動作と洗濯機内部の「**洗濯する**」という機能は完全に分離されているということだ。
実はこれはとても大事なことだ。
もし新しいバージョンの洗濯機が出てきて内部の構造は変わっても、僕たちはそれと関係なくいつものように「**スタート**」ボタンを押せばいい。
もし洗濯機の内部の配線が剥き出しになっていて中身を触り放題になっていたらとても危険だ。
間違った使い方をしてケガをしてしまうかもしれないし、中身の構造が変わるたびにユーザーも直接影響を受けることになってしまうからだ。

このように、必要な機能を1つにまとめた上で内部を隠蔽して外から見えなくすることを**カプセル化**という。
カプセル化はオブジェクト指向の大事な概念の一つだ。

ユーザーは正しい（限定された）使い方でしか内部の機能、データにはアクセスできないようにすることが望ましい。
洗濯機の例だと「スタートボタンを押す」以外の方法で勝手に洗濯機が起動しないようにしなければいけない。

これをクラスで考えると、クラス内部の変数は外からは隠蔽されていて、**決められた方法でしかアクセスできない**のが理想だ。
そしてこれを「決められた方法」を実現するのが`setter/getter`メソッドだ。

↓ちなみに、昔ツイッターでバズってた似たような例
（厳密にはこの考え方=オブジェクト指向ではないというツッコミはある）
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">このギタリストの機材説明、考え方が完全にオブジェクト指向なんだよなあ <a href="https://t.co/iqznh1HB4c">pic.twitter.com/iqznh1HB4c</a></p>&mdash; ヤギンゴ (@k_yagisan9) <a href="https://twitter.com/k_yagisan9/status/944573317129711617?ref_src=twsrc%5Etfw">December 23, 2017</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

(**ここでのカプセル化の説明の仕方についてコメントをもらったので最後に追記してます**)



# setterとgetterの役割

Javaなどの他のオブジェクト指向言語ではクラス内部の変数を隠蔽して「**setter/getterメソッドを使う**」のが一般的だ。
Pythonで書くとこんな感じになる。



``` Python
class Pokemon:
    def __init__(self, name='フシギダネ', weight=7.0):
        self.__name = name
        self.__weight = weight

    def set_name(self, name):
        self.__name = name

    def set_weight(self, weight):
        self.__weight = weight

    def get_name(self):
        return self.__name

    def get_weight(self):
        if self.__weight <= 0:
                raise ValueError(f'体重がマイナスになっちゃってるよぉ(>_<)')
        return self.__weight

pokemon = Pokemon()
pokemon.set_name('ピカチュウ')
pokemon.set_weight(6.0)

print("このポケモンの名前は", pokemon.get_name(), "だよ")
print("重さは", pokemon.get_weight(), " kgだよ")

print("重さを不正な値に変更しちゃうぞ！")
pokemon.set_weight(-999)
print("重さは", pokemon.get_weight(), " kgだよ")
```

``` bash:出力
このポケモンの名前は ピカチュウ だよ
重さは 6.0  kgだよ
重さを不正な値に変更しちゃうぞ！
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-40-7exxxxxxxxxx> in <module>
     27 print("重さを不正な値に変更しちゃうぞ！")
     28 pokemon.set_weight(-999)
---> 29 print("重さは", pokemon.get_weight(), " kgだよ")

<ipython-input-40-7exxxxxxxxxx> in get_weight(self)
     15     def get_weight(self):
     16         if self._weight <= 0:
---> 17                 raise ValueError(f'体重がマイナスになっちゃってるよぉ(>_<)')
     18         return self._weight
     19 

ValueError: 体重がマイナスになっちゃってるよぉ(>_<)
```

まず、クラス変数をpublicではなくprivateにして外部から隠蔽する。
これで先ほどの例のように直接`pokemon.name`に値を代入することができなくなる。
ちなみにPythonでは**変数の頭に__をつけることで変数をprivateにすることができる**。
これらの変数にアクセスするためにはクラスメソッドである`set_name()`と`get_name()`などを使う必要がある。
`set_name()`で名前をセットし、`get_name()`で値を取得する。
そして、`get_weight()`の中で不正な値をチェックできるようにする。

これによりクラス内部の変数のカプセル化に成功し、`setter/getter`メソッドを使ってのみアクセスできるようになった。
めでたしめでたし。

**ではなかった。**

Pythonでこの書き方が推奨されないのには2つの理由がある。

# 理由1：Pythonicでないから
`Pythonic（パイソニック）`とはPythonでコードを書く際の考え方、指針のことだ。
『Effective Python』という書籍によると

「*Pythonプログラマは、明示すること、複雑さよりは単純さを選ぶこと、可読性を最大化することを好みます*」

とある。要は「**Pythonを書くなら読みやすくて単純なコードを書こうぜ！**」ってことですね。
`setter/getter`を使った書き方は確かに安全に見えるけど、変数を取得するたびに毎回

``` python
pokemon.get_name()
```
みたいに書くのめんどくさいし読みづらいよねってことだ。
確かに最初に紹介した

``` python
pokemon.name
```
という書き方の方が簡単だし直感的だ。
最近ではIDEを使えば`setter/getter`を勝手に補完してくれたりはするものの、やはりコードの量が多くなってしまうことには変わらない。
Pythonを書く人(Pythonistaとか呼ばれる)からすると
「**こんな書き方はけしからん**」
ということだ。

この考え方は「**privateよりもpublicを使え**」という言葉にも当てはまる。
「`private`で制約を与えるんじゃなくて、`public`で自由にやろうよ。みんな大人なんだから。」
といった感じだ。
ちなみにEffective Pythonによるとこの「**大人なんだから**」というのはPythonのモットーらしい。
Pythonには`PEP8`というコードを書く際のスタイルガイドがあり、それを守ってみんなが行儀良くコード書けば
厳しい制約なんていらないよね？という考えがベースにある。

PEP8では具体的に
「**インデントの時はスペース4個分あけよう**」
「**一行は79文字より長くなりすぎないようにしよう**」
みたいな決まり事がまとめられている。
https://pep8-ja.readthedocs.io/ja/latest/

個人的にはこのような考え方の違いは「**校風**」みたいなものだと思っている。
例えば**偏差値の低い学校の校風は厳しい**。
制服の着こなしにルールがあったり髪を染めるのが禁止だったりする。
これらの厳しいルールがあるおかげで不良が暴れ回ったりしづらくなり治安が保たれるが、
ちゃんとルールを守ってる優等生にとっては息苦しい。

逆に**進学校の校風はかなり自由**で緩かったりする。
進学校の生徒はみんなルールを守るので私服OKだったり髪も自由に染めれたりする。

個人的には前者がJava、後者がPythonなどの言語のスタイルで、Pythonの**"自由な校風"**を一言
で表したのが**Pythonic**なのではないかなと思う。
（一応誤解のないように言っておくと、どちらが優れているということを言いたいわけではない。）


# 理由2：Pythonで完全なカプセル化は実現できないから

理由1での言語ごとのスタイルの違いは校風の違いのようなものだという話をした。
とはいえ進学校の生徒にも変わった奴がいて

「**自由な校風なんて知らねぇ！俺は明日から毎日制服で登校するんだ！**」

とか言い出す人がいるかもしれない。
その場合きっとクラスメイトはこう言うだろう。

「**そもそもうち制服ないけど何着るつもりなの？スーツ？それって厳密には制服じゃないから意味なくない？**」
と。
この例えがどれだけ的を射ているかはわからないが、実はPythonでsetter/getterメソッドを使っても
**Javaのような完全なカプセル化を実現することはできない**。
これが2つ目の理由だ。

PythonとJavaで比較してみる。
Javaには`public`, `private`, `protected`など様々なアクセス演算子がある。

- `public`: どこからでもアクセス可能
- `private`: 同じクラスからのみアクセス可能
- `protected`: クラスが違ってもサブクラス（継承したクラス）からならアクセス可能
- `デフォルト(アクセス演算子なし)`: 同じクラスのパッケージからはアクセス可能（= `package private`）

一方でPythonには`private`と`public`の2種類しかない。
ではPythonの`private`演算子でどれくらい隠蔽が可能なのか確かめてみる。
以下のように`Pokemon()`クラスの上位クラスとして`Ikimono()`クラスを考える。
そして`private`なクラス変数である`__name`を`Ikimono()`クラス側で定義し、`get_name()`を`Pokemon()`
側で定義する。
この時以下のコードはどのような挙動を示すだろうか？
上位クラスで`private`で定義した変数なので、それを継承した`Pokemon()`クラスからはアクセスできないようになっているはずだ。

``` Python
class Ikimono:
    def __init__(self, name='フシギダネ', weight=7.0):
        self.__name = name
        
    def set_name(self, name):
        self.__name = name
        
class Pokemon(Ikimono):
    def get_name(self):
        return self.__name

pokemon = Pokemon()
print("このポケモンの名前は", pokemon.get_name(), "だよ")
```
上のコードを実行すると確かに以下のエラーが出て失敗する。
一見すると隠蔽はできてそうだ。

``` python
AttributeError: 'Pokemon' object has no attribute '_Pokemon__name'
```
ところで、このエラーはどういう意味だろうか？
同じコードの最後で以下を実行してみよう。

``` python
print("このポケモンの名前は", pokemon._Ikimono__name, "だよ")
```

``` Python:出力
このポケモンの名前は フシギダネ だよ
```
**あれ、出力できちゃった。**

実は`Ikimono()`クラスで定義したprivate変数は内部では` _Ikimono__name`という名前で保存される。
これを継承先である`Pokemon()`クラスの`getter`メソッドからアクセスしようとすると` _Pokemon__name`を呼び出そうとして、無事失敗する。
ところが、これって`private`と言いつつただ頭にクラスの名前を付けてるだけなので、`pokemon._Ikimono__name`すれば本来隠蔽されて
取得できないはずの上位クラスの変数が取得できてしまった。

このようにPythonにおける隠蔽は**言語仕様上かなりガバガバ**なのである。
もちろんこれは理由１で述べた「**Pythonは自由で制約の少ない言語にしよう**」とマッチしている。
`private`と言いつつ間違った使い方をすれば外部からでも値を取得できるようになっているのだ。

「でもみんなそんな変な使い方しないよね？大人なんだから」
という声が聞こえそうだ。
他にも**属性アクセス**という仕組みを使って内部の変数を簡単に壊せちゃったりする。
（Effective Python第二版、項目42参照）

要するに**言語思想からしてsetter/getterは使うべきじゃない**し、そもそも**言語仕様上純粋なカプセル化は不可能**なのだ。


# 代替案：@propertyを使え
以上長々とPythonでgetter/setterメソッド定義すべきじゃない理由を述べてきた。
とはいえ全部publicでopenな変数しか使えないのは不便だ。
例えば上の`setterとgetterの役割`のコードでは「**体重が不正な値の時にはエラーを出す**」という仕組みをsetterメソッドで定義
していた。
Pythonでこのような処理をしたい場合は以下のように`@property`を使えば良い。
（出力は上の`setter/getterの役割`のコードと同じ）

``` python
class Pokemon:
    def __init__(self, name='フシギダネ', weight=7.0):
        self._name = name
        self._weight = weight

    @property
    def name(self):
        return self._name
    
    @property
    def weight(self):
        return self._weight

    @name.setter
    def name(self, name):
        self._name = name

    @weight.setter
    def weight(self, weight):
        if weight <= 0:
                raise ValueError(f'体重がマイナスになっちゃってるよぉ(>_<)')
        self._weight = weight

pokemon = Pokemon()
pokemon.name = 'ピカチュウ'
pokemon.weight = 6.0

print("このポケモンの名前は", pokemon.name, "だよ")
print("重さは", pokemon.weight, " kgだよ")

print("重さを不正な値に変更しちゃうぞ！")
pokemon.weight = -999
print("重さは", pokemon.weight, " kgだよ")
```

このように書けば`setter/getter`メソッドとほぼ同じ内容を実装することができる。
ここで注目したいのが変数を代入するときの処理が`pokemon.name`に直接代入していることだ。
これは見た目上はクラス変数に直接値を代入しているように見えて中ではsetter, getterの処理が走っているということだ。
これにより毎回`getter/setter`を長々と書く煩わしさから開放されつつも同様の処理を行うことができる。
このような`property`を使った方法はPythonに限らず最近はいろんな言語で実装されている
https://ja.wikipedia.org/wiki/%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3_(%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0)

ちなみにクラス内部の変数の頭に`_（アンダースコア）`が一つ付いてるのは`PEP8`で分かりやすいようにそうしましょうねと言われているだけで、
それ自体に意味はない。（前述のように2つ付けると`private`変数になる）

# 最後に

pythonにsetter/getterメソッドがないことや、private変数が推奨されていないことはちょっと調べてみるといくらでも出てくる。
でも「**なんでそうなってるのか？**」「**無理やりそういう書き方をするとどうなるのか？**」について突っ込んだ記事はあまりなかったので、
調べてまとめてみた。
結局**setter, getter使いたい時は@property使おうね**で全て終わってしまう記事なんだけど、この記事で少しでも理解が深まるといいなと
思う。

# 余談
僕が社会人一年目で新卒研修を受けていた時、研修にJavaがあった。
同期の一人が
「Javaってこんなめんどくさい書き方するんだ。Pythonなら一行で書けるのに。Javaはクソ」
とぶつぶつ言っていた。
それを聞いた他の同期が
「僕はJavaみたいな堅牢な言語、嫌いじゃないけどなぁ」
と返していた。
僕はその時
「堅牢？どういう意味だろう？ムズカシイ。」
と思ったのだけど、今回いろいろ調べてみてその意味が少しわかった気がした。

# 追記

コメントでカプセル化の説明について以下のようなコメントを貰った。


*そのまま洗濯機をモデルとしてクラスを作成するとした場合に、
洗濯機クラスはstart()メソッドだけがあり、それ以外に洗濯機を起動するmethodがないようにするのが、
理想だと思われます。
(中略)
setter,getterを通して洗濯機の内部のデータにアクセスすることを推奨しているように見えてしまいます。*


洗濯機を例に出した時に「スタートボタン以外で起動しないようにする」と説明していたのにsetter/getterで中の変数にアクセスできることを推奨するのはおかしいのでは？という指摘。
確かにその通りで、この記事ではJavaにおいてsetter/getterメソッドを使った上で完全なカプセル化が実現できるという前提で話を進めていたけど、setter/getterメソッドを使って直接データにアクセスできている時点で実は完全に内部を隠蔽できてはいない。
記事の中では`set_weight()`メソッドの中では不正な値を弾く処理をしているからまだいいけど、`get_name()`に至っては内部変数`name`をそのまま持ってきているので、厳密には隠蔽できてない。
このあたりを詰めていくと、Pythonでsetter/getterメソッドを使えないという話を超えて
「そもそもsetter/getterメソッドはいるのか？」
という論点になってしまいこの記事の要旨から外れてしまうため触れなかった。
ただ、洗濯機の例からsetter/getterメソッドを使ってカプセル化を実現するという説明になってしまってるのは確かに話が飛躍してしまっていておかしい。
（ごめんなさい。もっとうまい説明が見つかったらこっそり修正します。）

この記事では触れてこなかったのだけど、Javaなどの言語においてもsetter/getterメソッドなんてそもそも必要ないという人は一定数いるみたいだ。
例えばこの記事で触れている：https://qiita.com/katolisa/items/6cfd1a2a87058678d646