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で囲んでラップしているだけ。