Pythonでは、クラス作成時にプロパティを作れる。
まず、プロパティを作らずに、普通にクラスを作ると、
>>>class Position(object):
def __init__(self, x):
self.x = x
>>> p = Position(2)
>>> p
<__main__ .position="" 0x02cfa870="" at="" object="">
>>> p.x
2
となる。
これに、@propertyをつけてみる、
class Position(object):
def __init__(self, x):
self.x = x
@property
def x(self):
return self.x
とすると、
>>> p = Position(2)
Traceback (most recent call last):
File "", line 1, in
p = Position(2)
File "", line 3, in __init__
self.x = x
AttributeError: can't set attribute
となり、インスタンスを作成しようとするとアトリビュートエラーになる。
これは、@propertyデコレータをつけたメソッド(この場合はx)は、ゲッター、セッター、ディレーターがフックされるからである。
メソッドxに@propertyデコレータをつけたことで、インスタンス作成時に__init__が呼ばれ、その中でself.xへの代入を行われる。
この時、フックされたセッターを呼び出そうとするが、セッターが存在しないので、アトリビュートエラーが発生する。
そこで、次のようにセッターを作ってみると、
>>> class Position(object):
def __init__(self, x):
self.x = x
@property
def x(self):
return self.x
@x.setter
def x(self, x):
self.x = x
>>> p = Position(2)
Traceback (most recent call last):
File "", line 1, in
p = Position(2)
File "", line 3, in __init__
self.x = x
File "", line 10, in x
self.x = x
File "", line 10, in x
self.x = x
File "", line 10, in x
self.x = x
[Previous line repeated 491 more times]
RecursionError: maximum recursion depth exceeded while calling a Python object
となり、今度は再帰呼び出しの上限でエラーになる。これは、Positionインスタンスに値をセットする際に、__initi__中のself.xへの代入時に、xへのsetterが呼び出される。
そのsetter中で、さらにx.setterが呼び出されて・・・と、再帰呼び出しになってしまうためである。
なので、代入先の名前をself.xからself._xへと変更してやるとうまくいく。
>>> class Position(object):
def __init__(self, x):
self._x = x
@property
def x(self):
return self._x
@x.setter
def x(self, x):
self._x = x
>>> p = Position(2)
>>> p
<__main__ .position="" 0x02cfa650="" at="" object="">
>>> p.x
2
>>> p._x
2
>>> p.x = 10
>>> p.x
10
こうしてやると、うまくいく。
なお、上記のように、インスタンス中のプロパティを直接呼び出したり代入すことも出来る。
@propertyを使うメリットは、インスタンスのプロパティの参照時、代入時、削除時の挙動を変更することである。
また、次に示すが、プロパティへのアクセスは、p.getx()のようなメソッド呼び出しだと括弧が必要だが、@propertyを使うと、p.xとなり見やすくなる。
次のようにゲッター、セッター、 ディレーターを普通のメソッドとして実装すると、
>>> class Position(object):
def __init__(self, x):
self._x = x
def setx(self, x):
self._x = x
def getx(self):
return self._x
def delx(self):
del self._x
>>> p = Position(2)
>>> p.getx()
2
>>> p._x
2
>>> p.getx()
2
>>> p.setx(10)
>>> p.getx()
10
>>> p.delx()
>>> p.getx()
Traceback (most recent call last):
File "<pyshell#252>", line 1, in <module>
p.getx()
File "<pyshell#244>", line 7, in getx
return self._x
AttributeError: 'Position' object has no attribute '_x'
また、プロパティに変更があったとき、保持する値や他の値も自動的に更新したい時などにも使える。
>>> class Square(object):
def __init__(self, x):
self._x = x * x
@property
def x(self):
return self._x
@x.setter
def x(self, x):
self._x = x * x
>>> s = Square(4)
>>> s
<__main__ .square="" 0x02d6d390="" at="" object="">
>>> s.x
16
>>> s.x = 5
>>> s.x
25
0 件のコメント:
コメントを投稿