読者です 読者をやめる 読者になる 読者になる

ネストした関数を使ってpropertyを定義する

Python

 OreillyのPython Cookbook 2nd editionで、ネストした関数を使ってpropertyを定義するやり方を知り、目から鱗が落ちました。

class Cat(object):
    def _name():
        def fget(self):
            return self.__name
        def fset(self, value):
            self.__name = value
        return locals()
    name = property(**_name())

 ビルトイン関数localsの、非常に効果的な使い方だと思います。(localsが役に立っている例なんて、始めて見たよ!)
 ネストした関数でgetter, setterを定義しているので、見た目がC#っぽいのもポイントが高いですね。まあ、この点は個人的好みの問題だと思いますが。


 しかし、Python Cookbookのサンプルコードを試しているうちに、次のような汎用関数の方が、有用なのではないかと考え始めました。

def accessor(prop, *args):
    key = '_' + prop
    def set(self, value):
        if isinstance(value, args):
            self.__dict__[key] = value
        else:
            raise TypeError
    def get(self):
        try:
            return self.__dict__[key]
        except:
            raise AttributeError
    return property(get, set)


 このaccessor関数は、第一引数にオブジェクトの属性名、第二引数以下に属性がとりうる型を指定して呼び出し、デスクリプタ・オブジェクトpropertyを返します。このコードは動作しますが、引数の処理、例外処理がちょっと甘いです:-)
 使用例。

class Cat(object):
    name = accessor('name', str, unicode)

 このコードで定義したCatクラスのオブジェクトは、属性nameに、文字列、ユニコード文字列以外の値を設定しようとした場合、TypeErrorを発生させます。

>>> c = Cat()
>>> c.name = u'イッパイアッテナ' # OK
>>> print c.name
イッパイアッテナ
>>> c.name = 1                 # TypeError


 Python Cookbookは第二版になってから?、デスクリプタ・オブジェクト、メタクラスに関するレシピが充実しているように感じます。
 個人的に、デスクリプタ・オブジェクト、メタクラスの活用法については、まだ知識の及ばないところなので、このレシピは大変ためになりました。
 立ち読みじゃなくて、購入しようかなぁ・・・