一般的な書き方
クラス定義でgetter/setterなどのアクセサを一般的に書くと次のようになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class MyClass: def __init__(self): self._x = None def get_x(self): print("getting") return self._x def set_x(self, x): print("setting") self._x = x obj = MyClass() obj.get_x() obj.set_x(1) # getting # setting |
Pythonでは、メンバ変数(インスタンス変数)はパブリックアクセスが可能だが、メンバ変数への代入や参照を行った際に、裏でセッターやゲッターを呼び出すようにすることができる。また同じ手順で、インスタンスのdelete時の処理もプロパティとして登録できる。
1 2 3 4 5 6 7 8 9 10 |
obj = MyClass() obj.prop = val # ここで実際には、MyClass内で定義されたsome_setter(val)が実行される val = obj.prop # ここで実際には、MyClass内で定義されたval = some_getter()が実行される del obj # ここで実際には、MyClass内で定義されたsome_deleter()が実行される |
このように見かけ上の参照・代入・削除処理から各アクセサを呼び出すために、Pythonでは組み込み関数のproperty()
を使う方法と、@マークに続けたデコレータを使う方法がある。
property()関数
property関数を使うことで、getterなどをプロパティとして登録できる。次の例では、インスタンス変数_x
に関するgetter、setter、deleterをx
という名前のプロパティとして登録している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
class MyClass: def __init__(self): self._x = None def get_x(self): print("getting") return self._x def set_x(self, x): print("setting") self._x = x def del_x(self): print("deleting") del self._x x = property(get_x, set_x, del_x, "Property x") obj = MyClass() obj.x obj.x = 1 del obj.x # getting # setting # deleting |
property()
関数の引数は以下の通り。
property(fget=None, fset=None, fdel=None, doc=None)
fget
getterのメソッド名fset
setterのメソッド名fdel
deleterのメソッド名doc
プロパティの説明文(ドキュメント文字列)
propertyデコレータ
使い方
以下のように@マークに続けて書くデコレータでアクセサを定義できる。ただし以下の点に注意。
- デコレータの最初は、
@property
@property
で定義できるのはgetterのみ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
class MyClass: def __init__(self): self._x = None @property def x(self): print("getting") return self._x @x.setter def x(self, x): print("setting") self._x = x @x.deleter def x(self): print("deleting") del self._x obj = MyClass() obj.x obj.x = 1 del obj.x # getting # setting # deleting |
つまり、必ず最初に@property
で、かつgetterを定義しなければならない。
@propertyは最初でなければならない
たとえば次のように@property
がsetterよりも後にあると、”setterでxが定義されていませんよ”と怒られる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
class MyClass: def __init__(self): self._x = None @x.setter def x(self, x): print("setting") self._x = x @property def x(self): print("getting") return self._x @x.deleter def x(self): print("deleting") del self._x obj = MyClass() obj.x obj.x = 1 del obj.x # Traceback (most recent call last): # File "test.py", line 1, in <module> # class MyClass: # File "test.py", line 5, in MyClass # @x.setter # NameError: name 'x' is not defined |
@propertyはgetterしか定義できない
また@property
でsetterを定義し、@*.getter
でgetterを定義しようとすると、getterは実行されてsetterの方で”引数をセットできない”とエラーになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
class MyClass: def __init__(self): self._x = None @property def x(self, x): print("setting") self._x = x @x.getter def x(self): print("getting") return self._x @x.deleter def x(self): print("deleting") del self._x obj = MyClass() obj.x obj.x = 1 del obj.x # getting # Traceback (most recent call last): # File "test.py", line 22, in <module> # obj.x = 1 # AttributeError: can't set attribute |
@propertyでダミーのアクセサを定義する方法
@property
でダミーのアクセサを定義して、その後にgetterのデコレータで定義をするのは通る。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
class MyClass: def __init__(self): self._x = None @property def x(self): pass @x.getter def x(self): print("getting") return self._x @x.setter def x(self, x): print("setting") self._x = x @x.deleter def x(self): print("deleting") del self._x obj = MyClass() obj.x obj.x = 1 del obj.x # getting # setting # deleting |
property()でドキュメント文字列だけを定義する方法
また、property()
関数でメンバ変数のドキュメント文字列だけを定義したのちに、getter/setter/deleterのデコレータでそれぞれのメソッドを定義する方法もあるらしい。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
class MyClass: def __init__(self): self._x = None x = property(doc="property x") @x.getter def x(self): print("getting") return self._x @x.setter def x(self, x): print("setting") self._x = x @x.deleter def x(self): print("deleting") del self._x obj = MyClass() obj.x obj.x = 1 del obj.x |