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