オブジェクトもどきが成長してきた。

良いか悪いか微妙。

目的

foo.bar.baz = 10

を返すようなオブジェクトを手軽に作ること。

新しく追加

ほとんど同様(でも一部異なる)なオブジェクトを作りたくなった。テストのダミーとかで。
それをするために、almost_cloneという関数を書いた。

O = ObjectLike

oo = O(x=O(y=2))
eq_(oo.x.y, 2)
eq_almost_clone(oo, x__y=10).x.y, 10)
eq_(oo.x.y, 2)

この時、copy.copyを使ったらshallow copyだったのでdeepcopyを使った。
__deepcopy__を定義しなければ使えなかった。

似たようなオブジェクトを作る。

foo__bar__bazという感じで引数を渡してあげると、それに対応した値に変更される。

例えば、a.b.c.d.eのeの値だけを変えたオブジェクトを作りたい場合には(e=10に変更)

almost_clone(a, b__c__d__e=10)

という風に渡す。内部でコピーを作っているので、元のオブジェクト(a)の持つ値は変わらない。

だんだん長くなる。

from copy import deepcopy, copy

class ObjectLike(dict):
    __getattr__ = dict.__getitem__
    __setattr__ = dict.__setitem__

    def __deepcopy__(self, memo):
        obj = O()
        for k, v in self.items():
            if hasattr(v, "__deepcopy__"):
                obj[k] = deepcopy(v, memo)
            else:
                obj[k] = v
        return obj
O = ObjectLike
def almost_clone(obj, **kwargs):
    """ 
    oo = O(x=O(y=2))
    self.assertEqual(oo.x.y, 2)
    self.assertEqual(almost_clone(oo, x__y=10).x.y, 10)
    self.assertEqual(oo.x.y, 2)
    """
    def pair_to_accessor(target, k, v):
        tokens = k.split("__")
        if len(tokens) > 1:
            for token in tokens[0:-1]:
                target = target[token]
        last_token = tokens[-1]
        target[last_token] = v

    duped = deepcopy(obj)
    for k,v  in kwargs.items():
        pair_to_accessor(duped, k, v)
    return duped