プライベート変数

__spam 形式のマングリングが気に入らないから、
__getattribute__() や __setattr__() だのを使ってプライベート変数を実現しようと遊んでたけど。


そういえば Javascript でも似たよーなのあったなと思いだし、適当に書いてみた。
そしたら動いた。


こんなことも暫く思いつかなかったなんて……。
はふん!!



class Properties(object):
    """属性でアクセスできる辞書。
    
    ただのラッパ。
    
    >>> p = Properties(value=1)
    >>> p.value
    1
    """

    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)


class Class(object):
    """プライベート変数を持つクラス。

    >>> c1 = Class(1)
    >>> c1.value()
    1
    >>> c1.private
    Traceback (most recent call last):
        ...
    AttributeError: 'Class' object has no attribute 'private'
    >>> c1._
    Traceback (most recent call last):
        ...
    AttributeError: 'Class' object has no attribute '_'
    >>> c1.private_method()
    Traceback (most recent call last):
        ...
    AttributeError: 'Class' object has no attribute 'private_method'
    >>> c1.proxy_private_method()
    101
    >>> c2 = Class(2)
    >>> c2.value()
    2
    >>> c1.increment()
    >>> c1.value()
    2
    >>> c2.value()
    2
    """

    def closure():
        """クロージャ用の仮スコープ。"""

        _ = {} # クロージャ経由でアクセスされる変数。

        def __init__(self, value):
            _[self] = Properties(value=value)

        def __del__(self):
            del _[self]

        def value(self):
            return _[self].value

        def increment(self):
            _[self].value = _[self].value + 1

        def proxy_private_method(self):
            # private_method はクラスの辞書にないので、
            # _ と同じようにクロージャ経由でアクセスする。
            # 当然メソッド化もされていないので、 self を渡してやる。
            return private_method(self)

        def private_method(self):
            return _[self].value + 100

        # 公開するものだけ出す。 _ と private_method は出さない。
        return __init__, __del__, value, increment, proxy_private_method

    # closure() から取り出すものを取り出して
    __init__, __del__, value, increment, proxy_private_method = closure()
    # 用が終わったら削除する。
    del closure


以下、未検証。ただし検証する予定もない。


private_method の扱い。
クラス (正確には type のインスタンス) の辞書に入っている関数にアクセスするとき、
__getattribute__() によって、関数 (function) はメソッド (bound method) 化されてから返される。
private_method は、そもそも type のコンストラクタに渡されていない。
よってクラスの辞書にも入らず、メソッド化もされない。
だから self を渡してやる必要がある。


まあ、 private_method に限らずとも、スコープが解決できてるなら、同じようにアクセスできるけどね……。
(__getattribute__() は属性アクセスされるときに呼ばれるだけだから)