プロパティについて理解したのでまとめる
プロパティについて
研究室で質問されたのでPythonのプロパティについて解説しようと思う。
最初にプロパティとはなにかをIT用語辞典で引いていみると、
プロパティとは、オブジェクト指向プログラミングで使用される オブジェクトが保持している、そのオブジェクトの性質を表すデータ。 例えば、画像データのオブジェクトならば、高さや幅などのデータを プロパティとして持っている。
という定義になっている。
つまり、オブジェクト指向プログラミングでクラスのインスタンスから
アクセスできるimg.width
とかimg.height
とかの、width
やheight
のことである。
しかし、カプセル化の概念からすると、直接プロパティをいじれてしまうのは良くない。
そこで、オブジェクト指向プログラミングでは以下のコードのような
アクセサというメソッドとアクセス修飾子を使って、プロパティが
不用意にいじくりまわされるのを防いでいる。以下の例で言うと、
getWidth
やgetHeight
、setWidth
やsetHeight
がアクセサである。
class Image { private int width; private int height; public int getWidth() { return width; } public int getHeight() { return height; } public void setWidth(int width) { this.width = width; } public void setHeight(int height) { this.height = height; } } class Spam { public static void main(String args[]) { Image img = new Image(); img.setWidth(200); img.setHeight(100); System.out.println(img.getWidth()); System.out.println(img.getHeight()); } }
JavaやC++などではこのようにしてプロパティへアクセス制御をしているが、 ただアクセスを制御したいだけなら、確かに冗長な感じはする。
PythonやC#ではプロパティをインスタンス(使う側)からはメンバ変数に見えるが、 クラス(実装)ではコードブロック(メソッド)のようにして制御を行う。
具体的な例をPythonで示してみる。 以下のコードがプロパティを利用してアクセス制御を行っているコードになる。
#!/usr/bin/env python # -*- coding: utf-8 -*- class Image(object): def __init__(self): self._width = 300 self._height = 400 @property def width(self): return self._width @width.setter def width(self, width): self._width = width @property def height(self): return self._height @height.setter def height(self, height): self._height = height if __name__ == '__main__': img = Image() img.width = 200 img.height = 100 print img.width print img.height
クラスのメンバである_width
や_height
に対し、デコレータを利用してwidth
メソッドや
height
メソッドをproperty型に一度変換している(デコレータに関しては
前の記事を参照)。
property
の第一引数がgetter
になっているため、property
型でデコレートした
メソッドでは、そのプロパティが返したい値を返している。
また、Pythonのプロパティはsetterメソッドを持っているため、そのsetterメソッドで
メソッドをデコレートすることで、img.width = 200
のような形でアクセスできるように
なっている。
今回の例では代入と読み出しの両方を行っていたが、例えば、 読み出しは許可するが、代入を禁止したい場合、以下のようにsetterメソッドが 呼び出されたら例外を投げるなどをして、プロパティへのアクセス制御が行える。
class Image(object): ~~~ @width.setter def width(self, width): raise ValueError() ~~~
このようにプロパティを制御することで、クラスの設計者が意図しない値の代入や、 読み出し、不用意なプロパティの削除を防ぐことができる。