3秒でわかる
オブジェクトに結び付いた名前付きの値。Python では専用のデコレータでメソッドを値のように見せ、計算結果や入力検査を差し込めます。
もう少し詳しく
どういうものか
プロパティは、オブジェクトが持つ「名前と値の組」です。JavaScript ではオブジェクトのキーと値がそのままプロパティにあたり、user.name のように参照します。
Python では、これに加えて @property デコレータという仕組みがあります。メソッドとして定義した関数を、呼び出しかっこ無しで属性のように読める形に変えるものです。外から見れば単なる値、中では計算や検査が走っている、という状態を作れます。
なぜ必要か
最初は単純な属性で足りていたものが、後から「代入前に範囲を確かめたい」「保存している値から計算して返したい」と変わることがあります。ここで get_price() のようなメソッドに作り替えると、その属性を使っている全コードの書き方を変えることになります。
@property を使えば、外側の書き方 obj.price を保ったまま、中身だけを関数に差し替えられます。呼び出し側に影響を出さずに実装を変えられる点が価値です。
具体例
class Product:
def __init__(self, price):
self._price = price
@property
def price(self):
return self._price
@price.setter
def price(self, value):
if value < 0:
raise ValueError("価格に負の数は入れられません")
self._price = value
@property
def tax_included(self): # 保持せず毎回計算する
return int(self._price * 1.1)
p = Product(1000)
print(p.price) # 1000 かっこ不要
print(p.tax_included) # 1100
p.price = 1200 # setter が走る
# p.price = -5 # ValueErrorつまずきやすいところ
getter だけ定義して setter を書かないと、その属性は読み取り専用になります。代入した時点で AttributeError が出るため、意図した制限なのか書き忘れなのかをコメントで示しておくと後の混乱を防げます。
もう1つが無限再帰です。@property の中で同じ名前の属性に触れると、自分自身を呼び続けてスタックが尽きます。保持用の変数は _price のように別名にします。
JavaScript 側の落とし穴は、存在しないキーを読んでもエラーにならず undefined が返ることです。綴りを間違えた user.nmae は静かに undefined となり、表示が空欄になってはじめて気づきます。
似た用語との違い
| 呼び方 | 指すもの | 呼び出し |
|---|---|---|
| 属性 (attribute) | オブジェクトに紐づく値全般 | obj.x |
| プロパティ (Python) | @property で作った擬似的な属性 | obj.x |
| メソッド | オブジェクトに紐づく関数 | obj.f() |