やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

Pythonメタプログラミング学習。private変数とプロパティ(getter)を実装する

ググっても出てこなかったので。

成果物

GitHubPython.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、あたりのキーワードについて理解できていれば当然のように理解できるんだろうけど。そのハードルが高い。

どうして先述のコードでできるのか、絶対すぐ忘れる。まとめたいけど大変そう。