Torekatsuのブログ

日々学んだことを細々と

Pythonのpropertyでちょっとハマった

ハマったこと

クラスのインスタンス作成時にpropertyのsetterがうまく呼び出されない。

原因

クラスのinitで直接変数に値を代入していた

Propertyとは

Pythonでは変数のgetter・setterを簡便に設定できるpropertyという組み込み関数が有る。 他言語にあるgetNum()やsetNum()のような書き方をする必要がなく、 インスタンス変数へのアクセスをドット記法で行うことができるためコードがシンプルになる。

sample.py
class Example:
    def __init__(self, num):
        self._num = num

    @property
    def num(self):
        return self._num

    @num.setter
    def num(self, value):
        # 0以下だと例外
        if value <= 0:
            raise ValueError('num must be over 0!')
        self._num = value

上記のクラスを対話モードで使ってみる。

対話モード
>>> from sample import Example
>>> example = Example(10)
>>> example.num
10
>>> example.num = 1
>>> example.num
1
>>> example.num = 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "sample.py", line 12, in num
    raise ValueError("num must be over 0!")
ValueError: num must be over 0!
>>>

ただし、上記のコードではinit内で直接変数に値を代入しているため インスタンス作成時にはsetterが動作しない。

対話モード
>>> from sample import Example
>>> example = Example(0)
>>> example.num
>>> 0

そのためインスタンス作成時にsetterを動作させるには init内でsetterを呼び出す必要がある。

sample.py
class Example:
    def __init__(self, num):
        # self._num = num

        # setterを呼び出す
        self.num = num

    @property
    def num(self):
        return self._num

    @num.setter
    def num(self, value):
        if value <= 0:
            raise ValueError('num must be over 0!')
        self._num = value

同様に対話モードで使ってみる。 先ほどの例とは違い、インスタンス作成時にsetterが動作し 例外が投げられている。

対話モード
>>> from sample import Example
>>> example = Example(0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "sample.py", line 3, in __init__
    self.num = num
  File "sample.py", line 12, in num
    raise ValueError("num must be over 0!")
ValueError: num must be over 0!
>>>