pythonのプロパティのあれこれ

# はじめに
pythonの「property」について理解できていなかったので、
いろいろ調べたことをまとめていきたいと思います。

# そもそもプロパティってなんだっけ？
[簡単Python](https://www.amazon.co.jp/%E3%81%8B%E3%82%93%E3%81%9F%E3%82%93-Python-%E3%83%97%E3%83%AD%E3%82%B0%E3%83%9F%E3%83%B3%E3%82%B0%E3%81%AE%E6%95%99%E7%A7%91%E6%9B%B8-%E6%8E%8C%E7%94%B0-%E6%B4%A5%E8%80%B6%E4%B9%83/dp/4774195782)には、

>値をしっかり管理したいけど、インスタンス変数のように自然に値にアクセスできるようにもしたい。
この両者のいいとこ取りをしたのが、「プロパティ」。

と書かれています。
要は外から簡単に値を変得られない上に、取り出しは簡単にしたい時に役立つものと思ってもらったら良いです。

```python
def プロパティ名(self):
   処理

# プロパティの値を取り出すメソッドを定義する
@property
def プロパティ名(self):
  return # 値
  
# プロパティの値を設定
@プロパティ名.setter
def プロパティ名(self, value)
  # valueを設定する処理
```
プロパティはこんな感じで定義します。

```python
@property
```

を見たら「**ハイ！きた！**」と思ってください。

# インスタンス変数って？
プロパティは、インスタンス変数とつながりがあるので、ここではインスタンス変数について説明していきます。
インスタンス変数とは、クラスを実行する時に生成するインスタンスごとに独立して存在する変数です。
ちょっと難しいですよね。

```python
def __init__(self, message):
    self.message = message   # インスタンス変数 message
```
この「`self.message`」がインスタンス変数です。
次に全体の定義と動きを見ていきましょう。

```python

# インスタンス変数

class MyClass:
    def __init__(self, message):  # 初期化： インスタンス作成時に自動的に呼ばれる
        self.message = message    # インスタンス変数 message を宣言

    def print_message(self):  # インスタンス変数 message の値を表示する関数
        print(self.message)  # インスタンス変数 message にアクセスし表示する


instance = MyClass("instance")  # インスタンス instance を作成
python = MyClass("python")  # インスタンス python を作成

print(instance.message)  #instanceと表示される
print(python.message)  #pythonと表示される

instance.print_message()  #instanceと表示される
python.print_message()  #pythonと表示される

instance.message = 'Python' #self.messageの中身を「Python」に変更
print(instance.message)  # Pythonと表示される = 今まで「instance」と表示されていたのが変更されている

```
上のコードでわかるように簡単に値を取り出せますが、変更も簡単にできてしまいますよね。
変更が簡単にできてしまうと、間違えて変更してしまうなどの予期せぬことが起きてしまいます。
先ほども言いましたが、変更などの管理がしっかりできて、簡単に取り出せるものがプロパティです。

さっそく活用例を参考に理解を深めていきましょう。


# プロパティの活用例

```python
class PropertyClass:
      def __init__(self, msg):
        self.__message = msg
  
      def __str__(self):
        return self.message
    
      @property
      def message(self):
        return self.__message
    
      @message.setter
      def message(self, value):
        if value != '':
          self.__message = value
      

pc= PropertyClass('Hello')   #  ①
print(pc.message)   # Hello　②
pc.message = ''   # messageの中身を「Hello」から空文字列に変更   ③
print(pc)   # Hello  ④
pc.message = 'Bye!'   # messageの中身を「Hello」から「Bye!」に変更   ⑤
print(pc)   # Bye!   ⑥
```
①で「Hello」を引数として渡し、「self.message」の中身が「Hello」になります。
②messageメソッドを呼び出すと、「Hello」が戻り値として返ってきます。
③「message」の値を空文字列に変更しています。
④③で空文字列に変更したはずですが、再び「Hello」が表示されています。
なぜでしょう？
新たな値を設定する時、まず「@message.setter」のところで手続きが行われます。
今回はこのように空文字列以外の時変更受け付けますよと言っています。
よって空文字列は拒否されたため「Hello」が表示されたのです。
⑤懲りずに次は「Bye!」に変更しようとします。
⑥今回は空文字列ではないので、変更が許可されました。

また、今回は「__message」として、外部から直接アクセスされることはありません。
「\_\_」のように_が２つついているものは、外部からのアクセスができないので覚えておいてください。

このように値を新たに設定する時に、予想外のものが入ってしまわないように制御できるのがポイントです。

この章で大まかな動きは理解できたと思います。
次の章ではもう少し詳しく見ていきましょう。

# propertyとsetterの動き
propertyとsetterがついている部分がどのように呼び出されているの確認して、理解を深めましょう。

```python

class Myclass:
    def __init__(self, input_name):
        self.hidden_name = input_name

    @property
    def name(self):
        print('inside the getter')
        return self.hidden_name

    @name.setter
    def name(self, input_name):
        print('inseide the setter')
        self.hidden_name = input_name


mc = Myclass('Hello')   #「Hello」を引数として渡す  ①
print(mc.name)   #'Hello'と表示 + 'inside the getter'と表示　　②
mc.name = 'Python'   # 値を'Python'に変更 + 'inside the setter'と表示   ③
print(mc.name)   # 'Python'と表示 + 'inside the getter'と表示　　　④

```
②で、'inside the getter'と表示されていことから分かるように、'@property'が呼び出されていることがわかります。
一方、③で値を変更させるときは'inside the setter'とあるように、'@name.setter'の方が呼び出されています。
その結果、④では正常に値が変更されています。

このことから分かるように値を呼び出す時には'@property'が呼び出され、値を変更する時に'@〇〇.setter'が呼び出されます。
そして、予期せぬ値が入り込まないように、'setter'で値を制限することができます。
気付いた人もいるかもしれないですが、呼び出しの際にも'()'が最後に必要ないので簡単に呼び出すことができます。

# 最後に
今回はプロパティに関して説明してきました。
「簡単に呼び出せて、値の設定はきっちり行うもの」と認識してもらうと理解が早まるのかなとおおいます。
今後も記事はアップデートし行きます。
また、時間があれば他の記事も覗いていってください。