Pythonのススメ10

はじめに

Pythonでプログラムを書くにあたり、文法や言語仕様などの個人的なメモを記載する。
今回のネタはsetterとgetter。


propertyデコレータ

Pythonではsetterやgetterを定義するときはpropertyデコレータを用いる。
@propertyで定義されたメソッドはgetterとして機能する。
@属性名.setterで定義されたメソッドはsetterとして機能する。
※「Javaみたいにアクセッサメソッド作ればええやん、なんでわざわざ@propertyを使うんだ」と思われるかもしれないが(自分も最初はそう思った)、それについては後述する。

@property
def 属性名(self):
    return self._param

@属性名.setter
def 属性名(self, param):
    #何かしらの処理
    self._param = param

#引数名や属性名は任意。必ず「param」を使わないといけないというわけではない。

サンプルプログラムを以下に示す。
getterではセットされたメンバ変数の値(属性値)をそのまま返す。
setterでは引数のバリデーションチェックを行い、チェックに通れば値をセットする。通らなければエラーを投げる。

class MyClass:
    def __init__(self, param):
        self._param = param

    @property
    def param(self):
        return self._param

    @param.setter
    def param(self, param):
        if type(param) is int or type(param) is float:
            self._param = param
        else:
            raise ValueError("intかfloatを指定してください.")

MyClassを使う側のソースコード例は以下の通り。

cls = MyClass(100)
print(cls.param)
# コンソール上には100が表示される。
cls.param = 50
print(cls.param)
# コンソール上には50が表示される。


propertiesデコレータを使う意義

結論を一言で言い表すなら、「Pythonでサポートしている通常のメンバ変数へのアクセス方法を使いつつ、自分が意図する動作をプログラムとして記載する」だろうか。

そもそも、PythonJava等のように厳密な意味でのメンバの隠蔽が言語仕様としてできない。
Pythonで属性にアクセスする際は「オブジェクト名.メンバ変数名」と記述する。
なので、ただ値をsetしてgetするだけなら、以下のようにするだけでよい。

class MyClass:
    def __init__(self, param):
        self._param = param

cls = MyClass(100)
print(cls._param)
# コンソール上には100が表示される。

cls._param = 50
print(cls._param)
# コンソール上には50が表示される。

だが、これでは良くも悪くもどんな値でもメンバ変数にsetできてしまう。
例えばintかfloat以外の値はセットさせたくない場合、setterを用意してその中でバリデーションチェックを行う必要がある。

・・・
def set_param(self, param):
    if type(param) is int or type(param) is float:
        self._param = param
    else:
        raise ValueError("intかfloatを指定してください.")
    
def get_param(self):
    return self._param

しかし、先述の通り、値のセットや取得はわざわざ上に書いたようなアクセッサメソッドを経由しなくてもできる。
そのため、クラスを使う側が必ずしもアクセッサメソッドを呼んでset/getしてくれる保証もない。
(ドキュメントに残すことである程度抑止力にはなるだろうが、それでも限界はある)

そこで登場するのがpropertyデコレータである。
これを使うことで、メンバ変数に対して通常のアクセスの記述をしつつ専用メソッド経由で処理を行うことができるようになる。


終わりに

本投稿では、Pythonにおけるsetterとgetterの定義方法および、そうするべきであるという理由や背景を述べた。

  • setterやgetterを定義するときはpropertyデコレータを使う。
  • Javaのようにアクセッサメソッドを定義してもよいが、Pythonの性質上、メンバ変数へのアクセスは「オブジェクト名.メンバ変数名」で事足りるため、わざわざそのクラスを使う側がアクセッサメソッドを使ってくれる保証もない。
  • とはいえ、何でもかんでも値がセットされてしまうと困る場合もある。例えば、バリデーションチェックを行い、それに通った場合にのみ値をセットしたい場合もある。
  • そういった場合、propertyデコレータを使うことで、「Pythonでサポートしている通常のメンバ変数へのアクセス方法を使いつつ、自分が意図する動作をプログラムとして記載する」ことができるようになる。
  • サンプルプログラムでは、propertyデコレータを使って引数のバリデーションチェックを行いつつ、値のsetとgetを行った。


参考文献

あまり良くない書き方 その3 getter/setter - Python学習講座
プロパティ - Python学習講座