目次
(確認環境:Python 3)
プロパティとは
プロパティ(property)は、クラスのメンバ変数へのアクセスを、メソッド経由で行うようにカプセル化するための仕組みです。
外部からは通常の変数と同じに見えるが、内部ではメソッドとして動作します。
プロパティを使う主な目的
| バリデーション | setterで値を検証し、不正な値を弾く。例:負の半径を拒否するCircleクラス。 |
| 計算値の提供 | getterでその都度計算して返す。例:width×heightでareaを返すRectangleクラス。 |
| 読み取り専用 | getterのみ定義し、setterを書かないことで読み取り専用属性にできる。 |
| 後付けの変更 | 最初は普通の属性で実装し、後からプロパティに変えても呼び出し側のコードは変更不要。 |
デコレータ記法(@property)のサンプル
デコレータ記法はproperty()関数呼び出しの書き換え(ラップ)です。(シンタックスシュガー:糖衣構文)
class Color:
__name = None # 内部用の変数は頭に _ をつけるのが慣習
@property
def test1(self):
return self.__name
@test1.setter
def test1(self,a):
self.__name = a
@test1.deleter
def test1(self):
print("abc")
c1 = Color()
c1.test1 = "red" # setterの機能 10行目で値をセット
print(c1.test1) # red getterの機能 6行目で値を取得
del(c1.test1) # abc deleterの機能 14行目を実行
4行目は、@propertyがあります。5行目のtest1がプロパティ名になり、かつgetterの宣言になります。
8行目は、@test1.setterがあります。プロパティ名+setterです。setterの宣言になります。
12行目は、@test1.deleterがあります。プロパティ名+deleterです。deleterの宣言になります。
3つのデコレータ
| @property | getter。値を取得するときに呼ばれる。必須。 |
| @name.setter | setter。値をセットするときに呼ばれる。バリデーションを書くのに最適。 |
| @name.deleter | deleter。属性を削除するときに呼ばれる。使用頻度は低い。 |
property()関数を直接呼ぶ記法のサンプル
この書き方は古いスタイルです。
class Color:
__name = None # 内部用の変数は頭に _ をつけるのが慣習
def getName(self):
return self.__name
def setName(self,a):
self.__name = a
def delName(self):
print("abc")
p1 = property(getName,setName,delName)
c1 = Color()
c1.p1 = "red" # setterの機能 8行目で値をセット
print(c1.p1) # red getterの機能 5行目で値を取得
del(c1.p1) # abc deleterの機能 11行目を実行
13行目は、property関数です。
| class property(fget=None, fset=None, fdel=None) |
- fgetは、値を読み出す際の処理です。
- fsetは、値を代入する際の処理です。入力チェック(バリデーション)に最適。
- fdelはdelで削除された時の処理です。あまり使われません。
1つめの引数はゲッター(getter)です。値を取得するときに呼ばれます。
2つめの引数はセッター(setter)です。値をセットするときに呼ばれます。
3つめの引数はデリーター(deleter)です。属性を削除するときに呼ばれます。
プロパティ・デコレータ・property()関数の関係
| 概念 | 種別 | 役割 |
|---|---|---|
| プロパティ | 機能・概念 | 属性アクセスをメソッドで制御する仕組み |
| デコレータ | 構文・糖衣構文 | property()呼び出しを簡潔に書く記法 |
| property() | 組み込み関数 | プロパティオブジェクトを生成する実体 |
プロパティを使う理由:後付の変更のサンプル
最初はシンプルに普通の属性で実装していたとします。
class Person:
def __init__(self, name):
self.name = name # 普通の属性
p = Person("Alice")
print(p.name) # → Alice
後から名前は空文字NGというバリデーションを追加したとします。
class Person:
def __init__(self, name):
self.name = name # setterが呼ばれる
@property
def name(self):
return self._name
@name.setter
def name(self, value):
if not value:
raise ValueError("名前は空にできません")
self._name = value
上記のバリデーションを追加した場合でも、呼び出し側(外側)のコードは一切変更不要です。
p = Person("Alice")
print(p.name) # 変わらず動く
p.name = "Bob" # 変わらず動く
もしプロパティがなければ、バリデーションを追加するためにp.nameをp.get_name()のようなメソッド呼び出しに変更する必要が出てきます。
プロパティがあるおかげで、内部の実装だけ変えて外側のインターフェースは据え置きにできる、というのが「後付けの変更」の意味です。
関連の記事
Python クラスの仕組みとサンプル
Python クラスの継承の仕組みとサンプル
Python オーバーライドのサンプル
