いろいろ備忘録日記

主に .NET と Python絡みのメモを公開しています。

デコレータの利用

python2.4から、デコレータという概念が追加されています。


デコレータとは、GoFデザインパターンにある名前のやつと同じく
なにかを修飾する機能を持つものです。pythonの場合は、
デコレート対象が関数およびメソッドになります。


見た目的には、javaアノテーションに似てますね。


初期状態で、@classmethod, @staticmethodというデコレータが
用意されています。また、デコレータを自作することも可能です。
しかも、やり方はとても簡単。


デコレータを使うには、関数およびメソッドの前の行にて
@付きでデコレータ名を記述します。
以下、例です。

class A(object):
    def __init__(self):
        pass
    
    @classmethod
    def myClassMethod(self):
        """クラスメソッドです。"""
        pass

デコレータの正体は、実はただの関数です。
ただし、引数にデコレート対象の関数オブジェクトが
渡される事以外は、普通の関数です。


渡された関数オブジェクトに対して、どのような処理を行うかは
まったくの自由です。また、最後にreturnにてコール可能な
オブジェクトを返却すればいいだけです。


以下、サンプルです。
このサンプルでは、デコレート対象の関数オブジェクトに対して
動的にdetailというフィールドを追加しています。

#!/usr/bin/env python
# -*- coding: cp932 -*-
# vim:set ts=4 sw=4 et ws is nowrap ft=python:

def mydecorator(func_obj):
    """サンプルデコレータ.
    該当のファンクションオブジェクトに対して、detailフィールドを
    付加します。
    """
    code = func_obj.func_code
    func_obj.detail = \
            "<ファイル名:%s, 関数名:%s, 引数の数:%s>" % (code.co_filename, code.co_name, code.co_argcount)
    return func_obj


@mydecorator
def myfunc(x, y):
    """サンプル用の関数.
    この関数は、mydecoratorデコレータによって
    デコレートされます.
    """
    return x + y

if __name__ == '__main__':
    #
    # デコレートされたデータを表示
    #
    print myfunc.detail

    #
    # 普通にコール.
    #
    print myfunc(1, 10)

上記のソースを実行すると、myfunc関数オブジェクトには、detailというフィールドは
定義していないのに、実行時にはデコレータによって外から定義されるように
なります。


ちなみに、デコレータが返す値は、コール可能なもの(callable)なもので
あればいいので、やり方によっては全然別のオブジェクトを返す事も
可能ですね。