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