ググっても出てこなかったので。
成果物
Python.Metaclass.Private.20180223122844
概要
任意のテキストから、private変数とプロパティ(getter)を作る。
ソースコード
DbMeta.py
class DbMeta(type): def __new__(cls, name, bases, attrs): attrs['_{0}__secret'.format(name)] = 'my_secret' # Db.__secret 定義 return type.__new__(cls, name, bases, attrs) def __init__(cls, name, bases, attrs): setattr(cls, 'Secret', property(lambda cls: attrs['_{0}__secret'.format(name)])) # Db.Secretプロパティ定義
Db.py
from DbMeta import DbMeta class Db(metaclass=DbMeta): def show(self): print('----- Db.show(self) -----') print(dir(self)) print(self.__secret) print(self._Db__secret) print(self.Secret) assert(hasattr(self, '_Db__secret')) # self.__secret assert(hasattr(self, 'Secret')) # self.Secret
run.py
from Db import Db print('---- class obj ----') print(Db.Secret) print(Db._Db__secret) print('---- instance obj ----') db = Db() print(db.Secret) print(db._Db__secret) db.show()
実行
$ python3 run.py
---- class obj ---- <property object at 0x76ab1270> my_secret ---- instance obj ---- my_secret my_secret ----- Db.show(self) ----- ['Secret', '_Db__secret', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'show'] my_secret my_secret my_secret
なにが言いたいの?
Db
クラス内では変数もプロパティも定義していない。なのにDb
クラス内にすでに定義があり、参照できる! Oh! マジック! イリュージョン!
なぜかって? DbMeta
クラスで書いたからさ! Db
クラスに変数とプロパティを持つようにね! そう、クラスの作成をプログラミングできちゃうんだ☆
たとえば、DBなど外部リソースを元にして、プライベート変数とそのgetterを持ったクラスを自動作成できそうだね!
補足(privateなんて無い)
じつはPythonは言語仕様的にすべてpublic。self.__{変数名}
のように名前の前に__
を付けてもprivateになったわけではない。ins._{クラス名}__{変数名}
とすれば外部から参照できてしまう。詳しくは名前マングリング (name mangling) 参照。
ググるときに困るので、便宜上privateと言っているだけ。
所感
超苦労した。きっとPython理解してる人にとっては常識だからネット上に情報ないんだろうな。
メタプログラミングの情報はたくさんあるけど、private変数とproperty(getter)の組み合わせをメタプログラミングで実装するのは見つからなかった。
object, type, class, instance, スコープ(global, local), ディスクリプタ, metaclass、あたりのキーワードについて理解できていれば当然のように理解できるんだろうけど。そのハードルが高い。
どうして先述のコードでできるのか、絶対すぐ忘れる。まとめたいけど大変そう。