クラスのプロパティ

 プロパティとは、英語で書くとproperty、辞書で調べてみると「性質、特性」という意味らしい。パソコンを触っていると「フォルダのプロパティを確認する」みたいに使うことがある。
 プログラミングの世界だと、「クラスのプロパティ」という言葉でよく聞く。要はクラスのアトリビュートと最終目的は変わらないのです。設定する方法、参照する方法、削除する方法をちゃんと定義したアトリビュートをプロパティと呼びます。
 やれることがいっしょなら、簡単な方でやればいいじゃん。と思うのですが、C++言語で書かれたプログラムと互換性を持たせるには必要な機能です。

 組み込み関数 property

使い方は次のようにします。

 Property x = property([参照関数,[設定関数,[削除関数]]])

 あるプロパティxについて、参照関数、設定関数、削除関数を記述しておいて、最後にxに代入する形を取ります。具体例を見てみましょう。

#coding: shift-jis

class C(object):
  def __init__(self):
    self._x = None

  def getx(self):
    print "getxが呼ばれました。"
    return self._x

  def setx(self, value):
    print "setxが呼ばれました。"
    self._x = value

  def delx(self):
    print "delxが呼ばれました。"
    del self._x

  x = property(getx, setx, delx)

obj_c = C()

obj_c.x = 10 ←setx()が呼ばれます。
print obj_c.x ←getx()が呼ばれて、"10"が表示されます。
del obj_c.x ←delx()が呼ばれます。

raw_input()

出力画面:
 setxが呼ばれました。
 getxが呼ばれました。
 10
 delxが呼ばれました。
注意)
 例えば今回の場合、class Cのアトリビュートself._xをプロパティのxといっしょにすると、setx関数内でself.xの記述が出てきた段階で、これが変数のxなのか、プロパティのxなのか分からなくなってしまいます。実際はIron Pythonは、プロパティのxだと判断してsetxを呼び出します。そうするとまたxが出てきて・・・。つまり無限ループといって、いつまでも終わらない処理をし続けてしまいます。なので、絶対にアトリビュート名とプロパティ名は同名にしないように注意してください。

 プロパティをデコレータで書いてみる

   x = property(getx, setx, delx)

 上の行をみて、デコレータの記述に似ていると思いませんか。そうです。関数を並べていって最後にpropertyの記述をすることを嫌った誰かが、プロパティの記述をデコレータを使って表現できるようにしました。記述例は次のようになります。

#coding: shift-jis

class C(object):
  def __init__(self):
    self._x = None
  
  @property
  def x(self):
    print "getxが呼ばれました。"
    return self._x
  
  @x.setter
  def x(self, value):
    print "setxが呼ばれました。"
    self._x = value
  
  @x.deleter
  def x(self):
    print "delxが呼ばれました。"
    del self._x

obj_c = C()

obj_c.x = 10
print obj_c.x
del obj_c.x

raw_input()

出力画面:
 setxが呼ばれました。
 getxが呼ばれました。
 10
 delxが呼ばれました。
 出力結果は同じですが、記述が全然違いますね。ポイントは2つ、1つ目は@propertyの後には参照時の関数を記述します。その後propertyのsetter,deleterという形で記述します。2つ目は参照関数、設定関数、削除関数の関数名を同一のものにしてください。

 Pythonのマニュアルには、Ver2.6からgetter、setter、deleterの記述が出来るように変更したと書いてありました。setterやdeleterの記述は見たことがありますがgetterの記述がないので、本当かどうか確認してみました。

#coding: shift-jis

class C(object):
  def __init__(self):
    self._x = None

  x = property() ←予め空のプロパティを作り、順番に機能を付けていく。

  @x.getter
  def x(self):
    print "getxが呼ばれました。"
    return self._x

  @x.setter
  def x(self, value):
    print "setxが呼ばれました。"
    self._x = value

  @x.deleter
  def x(self):
    print "delxが呼ばれました。"
    del self._x

obj_c = C()

obj_c.x = 10
print obj_c.x
del obj_c.x

raw_input()

出力画面:
 setxが呼ばれました。
 getxが呼ばれました。
 10
 delxが呼ばれました。
おみごと、問題なく動きましたね。