python デコレータを使ってロギング処理を楽にする

from datetime import datetime


def logging(func):
    def wrapper(obj, *args, **kwds):
        print("{} {} {} {} args={} kwds={}".format(
            datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "INFO", func.__qualname__, "START", args, kwds))
        rtn = func(obj, *args, **kwds)
        print("{} {} {} {} return {}".format(
            datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "INFO", func.__qualname__, "END", rtn))
        return rtn
    return wrapper


class Hoge:

    @logging
    def add(self, x, y):
        return x + y

    @logging
    def sub(self, x, y):
        return x - y

これで以下のコードを実行すると、

hoge = Hoge()
hoge.add(1, 2)

以下が出力される

2015-03-30 00:48:10 INFO Hoge.add START args=(1, 2) kwds={}
2015-03-30 00:48:10 INFO Hoge.add END return 3

メソッド毎にロギング処理を自分で書くのは面倒だし、本質的でないコードが混じるのも好ましくない。
デコレータを付けるだけでメソッドの開始
/終了のログを出せるので、結構便利ではないだろうか。

python メタクラスを使ってクラスプロパティを実装する

pythonにはpropertyという組み込み関数が用意されており、これを使うことで(擬似的に)プライベートなインスタンス変数に対するgetter/setterのようなメソッドを作成する事ができる。

class Hoge:

    def __init__(self, name):
        self.__name = name

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, name):
        self.__name = name
        return

上記のコードにより、プライベートなインスタンス変数 __name に対して通常のインスタンス変数のようにアクセスできる。

hoge = Hoge("hoge")
print(hoge.name)    # => "hoge"
hoge.name = "fuga"
print(hoge.name)    # => "fuga"

まあ、元々pythonにはprivateやらpublicなんてものは無いので、上記の例だと普通に self.name という変数を使った場合と同じである。
しかし変数へのアクセスに何らかの処理をフックしたい、といった時には便利である。
(__setattr__ なんかでも似たような事ができるが、propertyは特定の変数に対してのみの定義である。)

ここから本題。
上記のpropertyはインスタンス変数にのみ有効である。クラス変数に対しても似た様な事ができないだろうか?(それが必要かは置いておいて)
例えば以下のようなコードを書いても想定通りの動作はしてくれない。

class Hoge:

    __NAME = "HOGE"

    @property
    @classmethod
    def name(cls):
        print("GET")
        return self.__NAME

    @name.setter
    @classmethod
    def name(cls, name):
        print("SET")
        self.__NAME = name
        return

そこで以下のようにメタクラスを利用すると、クラスプロパティを実装できる。

class ClassProperty(property):
    pass


class PropertyMeta(type):

    def __new__(cls, name, bases, namespace):
        props = [(k, v) for k, v in namespace.items() if type(v) == ClassProperty]
        for k, v in props:
            setattr(cls, k, v)
            del namespace[k]
        return type.__new__(cls, name, bases, namespace)


class Hoge(metaclass=PropertyMeta):

    __NAME = "HOGE"

    @ClassProperty
    def name(cls):
        print("GET")
        return cls.__NAME

    @name.setter
    def name(cls, name):
        print("SET")
        cls.__NAME = name
        return

python デコレータを使って例外処理をモジュール化する

pythonで普通に例外処理を書くと、以下のようになる。

def hoge(*args):
    try:
        # 処理
    except Exception as e:
        # 例外処理
    return

def fuga(*args):
    try:
        # 処理
    except Exception as e:
        # 例外処理
    return

def piyo(*args):
    try:
        # 処理
    except Exception as e:
        # 例外処理
    return

例外処理の内容が全て同じである場合(例えばログに吐き出すとか)、かなり冗長である。
そこでデコレータを使用すると以下のように書ける。

def exception(func):
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            # 例外処理
    return wrapper

@exception
def hoge(*args):
    # 処理
    return

@exception
def fuga(*args):
    # 処理
    return

@exception
def piyo(*args):
    # 処理
    return

やっている事は単純で、単にexceptionが受け取った関数をtry〜exceptで囲んでラップしているだけ。