プロパティは「属性参照によって任意の処理を走らせることができる」のが必要性です。単なるゲッター/セッターの代替ではありません。プロパティの役割は「メソッド呼び出しを属性参照に偽装すること」です。
(ついでにぞっとするかもしれないことを書いておくと、多くのPythonのライブラリでは「他の言語の文化ではゲッター/セッターを用いるかもしれないもの」を、プロパティですらない単なる属性として実装しています。「隠蔽? そんなの『まともに使えば』必要ないよ」という文化です。まあそれでも、必要と認められたときはゲッター/セッターの代替としてのプロパティも書かれるのですが)
たとえば、xy座標のあるグラフ上の図形みたいなオブジェクトを定義したとします。
python
1# ...はこのサンプルコードでは省略を表す
2class HogeFigure:
3 def __init__(self, x, y, ...): # x, yは中心座標とかそんなイメージ
4 self.x = x
5 self.y = y
6 ...
7
このオブジェクトから原点からの距離を取りたいとします。これは本質的に属性として持つ必要はありませんが、メソッド呼び出しにするべきでしょうか? 性質は属性です。
それでもまあ、移動しない図形なら__init__
にself.distance_from_origin = (self.x**2 + self.y**2)**0.5
とか書いておけばいいのかもしれません。図形の移動をサポートしようとすると厄介です。
動かすたびに計算し直すのは、頻繁に動かすなら無駄でしょう。しょっちゅう動かすけど、原点からの距離そのものは必要になる機会はそれほど多くないというシチュエーションなら、メソッド呼び出しにして必要なときだけ計算させたいところです。distance_from_origin
メソッドを用意すればいいのですね。
でも、性質としては属性なんじゃなかったっけ? なんで引数取る訳でもないのにメソッドとして呼ばないといけないのかわかりません。x
やy
と同様に扱えてほしいのです。
おまたせしました、プロパティの出番です。
python
1class HogeFigure:
2 def __init__(self, x, y, ...):
3 self.x = x
4 self.y = y
5 ...
6
7 @property
8 def distance_from_origin(self):
9 return (self.x**2 + self.y**2)**0.5
これはゲッターとして機能しますが、セッターもいけます。他の言語でのシチュエーションと同じです。図形を頻繁に動かすことは想定せず、distance_from_origin
を「普通の属性」として持つ設計にしたとしましょう。そしてx
, y
属性への代入は想定するとします。
x
とy
が書き換えられたらdistance_from_origin
も変えないといけません。まあ、上でやったみたいにこっちをプロパティにした方が良い気はしますが、お望みならx
とy
をプロパティにしてその中で再計算させることができる訳です。
あるいはまた異なったシチュエーションで、distance_from_origin
をプロパティにすると、こっちへ代入されたら整合が取れるようにx
とy
を変える、みたいな動作も考えられます。ちょっと実際のシチュエーションと挙動の想像が難しいですが(まあ同じ向きのベクトルとしてノルムだけ変えれば良いのかな)、動作としては成立します。
このように「属性アクセスでメソッドが呼び出せるので、色々なことができる」というか「メソッドを属性に見せられる」きがプロパティの存在意義で、上手く使うと単なるアクセス制御以外のことができます(必要なときに計算させるとか、データ整合性の確保をやったり、リソースの取得や解放に使うとか)。
参考
公式リファレンスの中にあるもの(めぼしいのはこれくらい?)
いにしえの昔にproperty
が追加されたときの説明
属性アクセス | What's New in Python 2.2 — Python 3.8.2 ドキュメント
もう少し詳しいやつ
デスクリプタ HowTo ガイド — Python 3.8.2 ドキュメント
同じような議論をしているページ
(以前ググったときは他にもいくつかあった気がするので、見つけたら追加)
propertyについて - podhmoの日記
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。